about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs285
-rw-r--r--compiler/rustc_ast/src/format.rs20
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs259
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs2235
-rw-r--r--compiler/rustc_macros/src/lib.rs11
-rw-r--r--compiler/rustc_macros/src/visitable.rs82
7 files changed, 1220 insertions, 1676 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8c2b521c560..97e07095875 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -28,7 +28,7 @@ use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::tagged_ptr::Tag;
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable};
 pub use rustc_span::AttrId;
 use rustc_span::source_map::{Spanned, respan};
 use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
@@ -39,6 +39,7 @@ use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter};
 use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
 use crate::util::parser::{ExprPrecedence, Fixity};
+use crate::visit::{AssocCtxt, BoundKind, LifetimeCtxt};
 
 /// A "Label" is an identifier of some point in sources,
 /// e.g. in the following code:
@@ -50,7 +51,7 @@ use crate::util::parser::{ExprPrecedence, Fixity};
 /// ```
 ///
 /// `'outer` is a label.
-#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)]
+#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq, Walkable)]
 pub struct Label {
     pub ident: Ident,
 }
@@ -63,7 +64,7 @@ impl fmt::Debug for Label {
 
 /// A "Lifetime" is an annotation of the scope in which variable
 /// can be used, e.g. `'a` in `&'a i32`.
-#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash, Walkable)]
 pub struct Lifetime {
     pub id: NodeId,
     pub ident: Ident,
@@ -87,7 +88,7 @@ impl fmt::Display for Lifetime {
 /// along with a bunch of supporting information.
 ///
 /// E.g., `std::cmp::PartialEq`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Path {
     pub span: Span,
     /// The segments in the path: the things separated by `::`.
@@ -211,7 +212,7 @@ pub fn join_path_idents(path: impl IntoIterator<Item = impl Borrow<Ident>>) -> S
 /// A segment of a path: an identifier, an optional lifetime, and a set of types.
 ///
 /// E.g., `std`, `String` or `Box<T>`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PathSegment {
     /// The identifier portion of this path segment.
     pub ident: Ident,
@@ -255,7 +256,7 @@ impl PathSegment {
 /// The generic arguments and associated item constraints of a path segment.
 ///
 /// E.g., `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericArgs {
     /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`.
     AngleBracketed(AngleBracketedArgs),
@@ -280,10 +281,10 @@ impl GenericArgs {
 }
 
 /// Concrete argument in the sequence of generic args.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericArg {
     /// `'a` in `Foo<'a>`.
-    Lifetime(Lifetime),
+    Lifetime(#[visitable(extra = LifetimeCtxt::GenericArg)] Lifetime),
     /// `Bar` in `Foo<Bar>`.
     Type(P<Ty>),
     /// `1` in `Foo<1>`.
@@ -301,7 +302,7 @@ impl GenericArg {
 }
 
 /// A path like `Foo<'a, T>`.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct AngleBracketedArgs {
     /// The overall span.
     pub span: Span,
@@ -310,7 +311,7 @@ pub struct AngleBracketedArgs {
 }
 
 /// Either an argument for a generic parameter or a constraint on an associated item.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AngleBracketedArg {
     /// A generic argument for a generic parameter.
     Arg(GenericArg),
@@ -340,7 +341,7 @@ impl From<ParenthesizedArgs> for P<GenericArgs> {
 }
 
 /// A path like `Foo(A, B) -> C`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ParenthesizedArgs {
     /// ```text
     /// Foo(A, B) -> C
@@ -376,7 +377,7 @@ impl ParenthesizedArgs {
 pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId};
 
 /// Modifiers on a trait bound like `[const]`, `?` and `!`.
-#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Walkable)]
 pub struct TraitBoundModifiers {
     pub constness: BoundConstness,
     pub asyncness: BoundAsyncness,
@@ -391,10 +392,10 @@ impl TraitBoundModifiers {
     };
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericBound {
     Trait(PolyTraitRef),
-    Outlives(Lifetime),
+    Outlives(#[visitable(extra = LifetimeCtxt::Bound)] Lifetime),
     /// Precise capturing syntax: `impl Sized + use<'a>`
     Use(ThinVec<PreciseCapturingArg>, Span),
 }
@@ -429,7 +430,7 @@ impl fmt::Display for ParamKindOrd {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericParamKind {
     /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`).
     Lifetime,
@@ -445,11 +446,12 @@ pub enum GenericParamKind {
     },
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct GenericParam {
     pub id: NodeId,
     pub ident: Ident,
     pub attrs: AttrVec,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
     pub is_placeholder: bool,
     pub kind: GenericParamKind,
@@ -470,7 +472,7 @@ impl GenericParam {
 
 /// Represents lifetime, type and const parameters attached to a declaration of
 /// a function, enum, trait, etc.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct Generics {
     pub params: ThinVec<GenericParam>,
     pub where_clause: WhereClause,
@@ -478,7 +480,7 @@ pub struct Generics {
 }
 
 /// A where-clause in a definition.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct WhereClause {
     /// `true` if we ate a `where` token.
     ///
@@ -496,7 +498,7 @@ impl WhereClause {
 }
 
 /// A single predicate in a where-clause.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WherePredicate {
     pub attrs: AttrVec,
     pub kind: WherePredicateKind,
@@ -506,7 +508,7 @@ pub struct WherePredicate {
 }
 
 /// Predicate kind in where-clause.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum WherePredicateKind {
     /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
     BoundPredicate(WhereBoundPredicate),
@@ -519,42 +521,45 @@ pub enum WherePredicateKind {
 /// A type bound.
 ///
 /// E.g., `for<'c> Foo: Send + Clone + 'c`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereBoundPredicate {
     /// Any generics from a `for` binding.
     pub bound_generic_params: ThinVec<GenericParam>,
     /// The type being bounded.
     pub bounded_ty: P<Ty>,
     /// Trait and lifetime bounds (`Clone + Send + 'static`).
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
 }
 
 /// A lifetime predicate.
 ///
 /// E.g., `'a: 'b + 'c`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereRegionPredicate {
+    #[visitable(extra = LifetimeCtxt::Bound)]
     pub lifetime: Lifetime,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
 }
 
 /// An equality predicate (unsupported).
 ///
 /// E.g., `T = int`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereEqPredicate {
     pub lhs_ty: P<Ty>,
     pub rhs_ty: P<Ty>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Crate {
-    pub attrs: AttrVec,
-    pub items: ThinVec<P<Item>>,
-    pub spans: ModSpans,
     /// Must be equal to `CRATE_NODE_ID` after the crate root is expanded, but may hold
     /// expansion placeholders or an unassigned value (`DUMMY_NODE_ID`) before that.
     pub id: NodeId,
+    pub attrs: AttrVec,
+    pub items: ThinVec<P<Item>>,
+    pub spans: ModSpans,
     pub is_placeholder: bool,
 }
 
@@ -608,7 +613,7 @@ pub enum MetaItemInner {
 /// A block (`{ .. }`).
 ///
 /// E.g., `{ .. }` as in `fn foo() { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Block {
     /// The statements in the block.
     pub stmts: ThinVec<Stmt>,
@@ -622,7 +627,7 @@ pub struct Block {
 /// A match pattern.
 ///
 /// Patterns appear in match statements and some other contexts, such as `let` and `if let`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Pat {
     pub id: NodeId,
     pub kind: PatKind,
@@ -770,7 +775,7 @@ impl From<P<Pat>> for Pat {
 /// Patterns like the fields of `Foo { x, ref y, ref mut z }`
 /// are treated the same as `x: x, y: ref y, z: ref mut z`,
 /// except when `is_shorthand` is true.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PatField {
     /// The identifier for the field.
     pub ident: Ident,
@@ -784,7 +789,7 @@ pub struct PatField {
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum ByRef {
     Yes(Mutability),
     No,
@@ -806,7 +811,7 @@ impl ByRef {
 /// `.0` is the by-reference mode (`ref`, `ref mut`, or by value),
 /// `.1` is the mutability of the binding.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub struct BindingMode(pub ByRef, pub Mutability);
 
 impl BindingMode {
@@ -829,7 +834,7 @@ impl BindingMode {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeEnd {
     /// `..=` or `...`
     Included(RangeSyntax),
@@ -837,7 +842,7 @@ pub enum RangeEnd {
     Excluded,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeSyntax {
     /// `...`
     DotDotDot,
@@ -848,7 +853,7 @@ pub enum RangeSyntax {
 /// All the different flavors of pattern that Rust recognizes.
 //
 // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum PatKind {
     /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`.
     Missing,
@@ -930,7 +935,7 @@ pub enum PatKind {
 }
 
 /// Whether the `..` is present in a struct fields pattern.
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
 pub enum PatFieldsRest {
     /// `module::StructName { field, ..}`
     Rest,
@@ -943,7 +948,7 @@ pub enum PatFieldsRest {
 /// The kind of borrow in an `AddrOf` expression,
 /// e.g., `&place` or `&raw const place`.
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum BorrowKind {
     /// A normal borrow, `&$expr` or `&mut $expr`.
     /// The resulting type is either `&'a T` or `&'a mut T`
@@ -959,7 +964,7 @@ pub enum BorrowKind {
     Pin,
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum BinOpKind {
     /// The `+` operator (addition)
     Add,
@@ -1089,7 +1094,7 @@ impl From<AssignOpKind> for BinOpKind {
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum AssignOpKind {
     /// The `+=` operator (addition)
     AddAssign,
@@ -1141,7 +1146,7 @@ pub type AssignOp = Spanned<AssignOpKind>;
 /// Unary operator.
 ///
 /// Note that `&data` is not an operator, it's an `AddrOf` expression.
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum UnOp {
     /// The `*` operator for dereferencing
     Deref,
@@ -1215,7 +1220,7 @@ impl Stmt {
 }
 
 // Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum StmtKind {
     /// A local (let) binding.
     Let(P<Local>),
@@ -1231,7 +1236,7 @@ pub enum StmtKind {
     MacCall(P<MacCallStmt>),
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MacCallStmt {
     pub mac: P<MacCall>,
     pub style: MacStmtStyle,
@@ -1239,7 +1244,7 @@ pub struct MacCallStmt {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum MacStmtStyle {
     /// The macro statement had a trailing semicolon (e.g., `foo! { ... };`
     /// `foo!(...);`, `foo![...];`).
@@ -1253,7 +1258,7 @@ pub enum MacStmtStyle {
 }
 
 /// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Local {
     pub id: NodeId,
     pub super_: Option<Span>,
@@ -1266,7 +1271,7 @@ pub struct Local {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum LocalKind {
     /// Local declaration.
     /// Example: `let x;`
@@ -1306,7 +1311,7 @@ impl LocalKind {
 ///     _ => { println!("no match!") },
 /// }
 /// ```
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Arm {
     pub attrs: AttrVec,
     /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`.
@@ -1321,7 +1326,7 @@ pub struct Arm {
 }
 
 /// A single field in a struct expression, e.g. `x: value` and `y` in `Foo { x: value, y }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ExprField {
     pub attrs: AttrVec,
     pub id: NodeId,
@@ -1332,13 +1337,13 @@ pub struct ExprField {
     pub is_placeholder: bool,
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
 pub enum BlockCheckMode {
     Default,
     Unsafe(UnsafeSource),
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
 pub enum UnsafeSource {
     CompilerGenerated,
     UserProvided,
@@ -1349,7 +1354,7 @@ pub enum UnsafeSource {
 /// These are usually found nested inside types (e.g., array lengths)
 /// or expressions (e.g., repeat counts), and also used to define
 /// explicit discriminant values for enum variants.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AnonConst {
     pub id: NodeId,
     pub value: P<Expr>,
@@ -1633,7 +1638,7 @@ impl From<P<Expr>> for Expr {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Closure {
     pub binder: ClosureBinder,
     pub capture_clause: CaptureBy,
@@ -1649,7 +1654,7 @@ pub struct Closure {
 }
 
 /// Limit types of a range (inclusive or exclusive).
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeLimits {
     /// Inclusive at the beginning, exclusive at the end.
     HalfOpen,
@@ -1680,7 +1685,7 @@ pub struct MethodCall {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum StructRest {
     /// `..x`.
     Base(P<Expr>),
@@ -1690,7 +1695,7 @@ pub enum StructRest {
     None,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct StructExpr {
     pub qself: Option<P<QSelf>>,
     pub path: Path,
@@ -1880,14 +1885,14 @@ pub enum ExprKind {
 }
 
 /// Used to differentiate between `for` loops and `for await` loops.
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub enum ForLoopKind {
     For,
     ForAwait,
 }
 
 /// Used to differentiate between `async {}` blocks and `gen {}` blocks.
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub enum GenBlockKind {
     Async,
     Gen,
@@ -1912,7 +1917,7 @@ impl GenBlockKind {
 
 /// Whether we're unwrapping or wrapping an unsafe binder
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum UnsafeBinderCastKind {
     // e.g. `&i32` -> `unsafe<'a> &'a i32`
     Wrap,
@@ -1934,7 +1939,7 @@ pub enum UnsafeBinderCastKind {
 ///  ^~~~~    ^
 ///  ty       position = 0
 /// ```
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct QSelf {
     pub ty: P<Ty>,
 
@@ -1946,7 +1951,7 @@ pub struct QSelf {
 }
 
 /// A capture clause used in closures and `async` blocks.
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum CaptureBy {
     /// `move |x| y + x`.
     Value {
@@ -1967,7 +1972,7 @@ pub enum CaptureBy {
 }
 
 /// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum ClosureBinder {
     /// The binder is not present, all closure lifetimes are inferred.
     NotPresent,
@@ -1993,7 +1998,7 @@ pub enum ClosureBinder {
 
 /// Represents a macro invocation. The `path` indicates which macro
 /// is being invoked, and the `args` are arguments passed to it.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MacCall {
     pub path: Path,
     pub args: P<DelimArgs>,
@@ -2006,7 +2011,7 @@ impl MacCall {
 }
 
 /// Arguments passed to an attribute macro.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AttrArgs {
     /// No arguments: `#[attr]`.
     Empty,
@@ -2041,7 +2046,7 @@ impl AttrArgs {
 }
 
 /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub struct DelimArgs {
     pub dspan: DelimSpan,
     pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs
@@ -2057,7 +2062,7 @@ impl DelimArgs {
 }
 
 /// Represents a macro definition.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub struct MacroDef {
     pub body: P<DelimArgs>,
     /// `true` if macro was defined with `macro_rules`.
@@ -2065,7 +2070,7 @@ pub struct MacroDef {
 }
 
 #[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum StrStyle {
     /// A regular string, like `"foo"`.
     Cooked,
@@ -2076,7 +2081,7 @@ pub enum StrStyle {
 }
 
 /// The kind of match expression
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
 pub enum MatchKind {
     /// match expr { ... }
     Prefix,
@@ -2085,7 +2090,7 @@ pub enum MatchKind {
 }
 
 /// The kind of yield expression
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum YieldKind {
     /// yield expr { ... }
     Prefix(Option<P<Expr>>),
@@ -2136,7 +2141,7 @@ pub struct MetaItemLit {
 }
 
 /// Similar to `MetaItemLit`, but restricted to string literals.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub struct StrLit {
     /// The original literal as written in source code.
     pub symbol: Symbol,
@@ -2265,7 +2270,7 @@ impl LitKind {
 
 // N.B., If you change this, you'll probably want to change the corresponding
 // type structure in `middle/ty.rs` as well.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MutTy {
     pub ty: P<Ty>,
     pub mutbl: Mutability,
@@ -2389,7 +2394,7 @@ impl UintTy {
 /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy`
 /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`)
 /// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`)
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AssocItemConstraint {
     pub id: NodeId,
     pub ident: Ident,
@@ -2398,7 +2403,7 @@ pub struct AssocItemConstraint {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum Term {
     Ty(P<Ty>),
     Const(AnonConst),
@@ -2417,7 +2422,7 @@ impl From<AnonConst> for Term {
 }
 
 /// The kind of [associated item constraint][AssocItemConstraint].
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AssocItemConstraintKind {
     /// An equality constraint for an associated item (e.g., `AssocTy = Ty` in `Trait<AssocTy = Ty>`).
     ///
@@ -2427,10 +2432,13 @@ pub enum AssocItemConstraintKind {
     /// bindings*. Similarly with associated const equality constraints and *associated const bindings*.
     Equality { term: Term },
     /// A bound on an associated type (e.g., `AssocTy: Bound` in `Trait<AssocTy: Bound>`).
-    Bound { bounds: GenericBounds },
+    Bound {
+        #[visitable(extra = BoundKind::Bound)]
+        bounds: GenericBounds,
+    },
 }
 
-#[derive(Encodable, Decodable, Debug)]
+#[derive(Encodable, Decodable, Debug, Walkable)]
 pub struct Ty {
     pub id: NodeId,
     pub kind: TyKind,
@@ -2474,7 +2482,7 @@ impl Ty {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FnPtrTy {
     pub safety: Safety,
     pub ext: Extern,
@@ -2485,7 +2493,7 @@ pub struct FnPtrTy {
     pub decl_span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct UnsafeBinderTy {
     pub generic_params: ThinVec<GenericParam>,
     pub inner_ty: P<Ty>,
@@ -2494,7 +2502,7 @@ pub struct UnsafeBinderTy {
 /// The various kinds of type recognized by the compiler.
 //
 // Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum TyKind {
     /// A variable-length slice (`[T]`).
     Slice(P<Ty>),
@@ -2503,11 +2511,11 @@ pub enum TyKind {
     /// A raw pointer (`*const T` or `*mut T`).
     Ptr(MutTy),
     /// A reference (`&'a T` or `&'a mut T`).
-    Ref(Option<Lifetime>, MutTy),
+    Ref(#[visitable(extra = LifetimeCtxt::Ref)] Option<Lifetime>, MutTy),
     /// A pinned reference (`&'a pin const T` or `&'a pin mut T`).
     ///
     /// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`.
-    PinnedRef(Option<Lifetime>, MutTy),
+    PinnedRef(#[visitable(extra = LifetimeCtxt::Ref)] Option<Lifetime>, MutTy),
     /// A function pointer type (e.g., `fn(usize) -> bool`).
     FnPtr(P<FnPtrTy>),
     /// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`).
@@ -2523,14 +2531,14 @@ pub enum TyKind {
     Path(Option<P<QSelf>>, Path),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(GenericBounds, TraitObjectSyntax),
+    TraitObject(#[visitable(extra = BoundKind::TraitObject)] GenericBounds, TraitObjectSyntax),
     /// An `impl Bound1 + Bound2 + Bound3` type
     /// where `Bound` is a trait or a lifetime.
     ///
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds),
+    ImplTrait(NodeId, #[visitable(extra = BoundKind::Impl)] GenericBounds),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
@@ -2608,7 +2616,7 @@ impl TyKind {
 }
 
 /// A pattern type pattern.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TyPat {
     pub id: NodeId,
     pub kind: TyPatKind,
@@ -2619,7 +2627,7 @@ pub struct TyPat {
 /// All the different flavors of pattern that Rust recognizes.
 //
 // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum TyPatKind {
     /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
     Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),
@@ -2631,7 +2639,7 @@ pub enum TyPatKind {
 }
 
 /// Syntax used to declare a trait object.
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 #[repr(u8)]
 pub enum TraitObjectSyntax {
     // SAFETY: When adding new variants make sure to update the `Tag` impl.
@@ -2658,10 +2666,10 @@ unsafe impl Tag for TraitObjectSyntax {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum PreciseCapturingArg {
     /// Lifetime parameter.
-    Lifetime(Lifetime),
+    Lifetime(#[visitable(extra = LifetimeCtxt::GenericArg)] Lifetime),
     /// Type or const parameter.
     Arg(Path, NodeId),
 }
@@ -2669,7 +2677,7 @@ pub enum PreciseCapturingArg {
 /// Inline assembly operand explicit register or register class.
 ///
 /// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub enum InlineAsmRegOrRegClass {
     Reg(Symbol),
     RegClass(Symbol),
@@ -2738,7 +2746,7 @@ impl std::fmt::Debug for InlineAsmOptions {
     }
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic, Walkable)]
 pub enum InlineAsmTemplatePiece {
     String(Cow<'static, str>),
     Placeholder { operand_idx: usize, modifier: Option<char>, span: Span },
@@ -2786,7 +2794,7 @@ impl InlineAsmTemplatePiece {
 /// `DefCollector`. Instead this is deferred until AST lowering where we
 /// lower it to an `AnonConst` (for functions) or a `Path` (for statics)
 /// depending on what the path resolves to.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct InlineAsmSym {
     pub id: NodeId,
     pub qself: Option<P<QSelf>>,
@@ -2796,7 +2804,7 @@ pub struct InlineAsmSym {
 /// Inline assembly operand.
 ///
 /// E.g., `out("eax") result` as in `asm!("mov eax, 2", out("eax") result)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum InlineAsmOperand {
     In {
         reg: InlineAsmRegOrRegClass,
@@ -2841,7 +2849,7 @@ impl InlineAsmOperand {
     }
 }
 
-#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum AsmMacro {
     /// The `asm!` macro
     Asm,
@@ -2880,13 +2888,14 @@ impl AsmMacro {
 /// Inline assembly.
 ///
 /// E.g., `asm!("NOP");`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct InlineAsm {
     pub asm_macro: AsmMacro,
     pub template: Vec<InlineAsmTemplatePiece>,
     pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
     pub operands: Vec<(InlineAsmOperand, Span)>,
     pub clobber_abis: Vec<(Symbol, Span)>,
+    #[visitable(ignore)]
     pub options: InlineAsmOptions,
     pub line_spans: Vec<Span>,
 }
@@ -2894,7 +2903,7 @@ pub struct InlineAsm {
 /// A parameter in a function header.
 ///
 /// E.g., `bar: usize` as in `fn foo(bar: usize)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Param {
     pub attrs: AttrVec,
     pub ty: P<Ty>,
@@ -3022,7 +3031,7 @@ impl Param {
 ///
 /// Please note that it's different from `FnHeader` structure
 /// which contains metadata about function safety, asyncness, constness and ABI.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FnDecl {
     pub inputs: ThinVec<Param>,
     pub output: FnRetTy,
@@ -3038,7 +3047,7 @@ impl FnDecl {
 }
 
 /// Is the trait definition an auto trait?
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum IsAuto {
     Yes,
     No,
@@ -3046,7 +3055,7 @@ pub enum IsAuto {
 
 /// Safety of items.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum Safety {
     /// `unsafe` an item is explicitly marked as `unsafe`.
     Unsafe(Span),
@@ -3062,7 +3071,7 @@ pub enum Safety {
 /// Coroutine markers are things that cause the function to generate a coroutine, such as `async`,
 /// which makes the function return `impl Future`, or `gen`, which makes the function return `impl
 /// Iterator`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum CoroutineKind {
     /// `async`, which returns an `impl Future`.
     Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
@@ -3111,7 +3120,7 @@ impl CoroutineKind {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum Const {
     Yes(Span),
     No,
@@ -3119,13 +3128,13 @@ pub enum Const {
 
 /// Item defaultness.
 /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532).
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum Defaultness {
     Default(Span),
     Final,
 }
 
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum ImplPolarity {
     /// `impl Trait for Type`
     Positive,
@@ -3144,7 +3153,7 @@ impl fmt::Debug for ImplPolarity {
 
 /// The polarity of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundPolarity {
     /// `Type: Trait`
     Positive,
@@ -3166,7 +3175,7 @@ impl BoundPolarity {
 
 /// The constness of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundConstness {
     /// `Type: Trait`
     Never,
@@ -3188,7 +3197,7 @@ impl BoundConstness {
 
 /// The asyncness of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundAsyncness {
     /// `Type: Trait`
     Normal,
@@ -3205,7 +3214,7 @@ impl BoundAsyncness {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FnRetTy {
     /// Returns type is not specified.
     ///
@@ -3225,14 +3234,14 @@ impl FnRetTy {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum Inline {
     Yes,
     No,
 }
 
 /// Module item kind.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum ModKind {
     /// Module with inlined definition `mod foo { ... }`,
     /// or with definition outlined to a separate file `mod foo;` and already loaded from it.
@@ -3243,7 +3252,7 @@ pub enum ModKind {
     Unloaded,
 }
 
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct ModSpans {
     /// `inner_span` covers the body of the module; for a file module, its the whole file.
     /// For an inline module, its the span inside the `{ ... }`, not including the curly braces.
@@ -3254,7 +3263,7 @@ pub struct ModSpans {
 /// Foreign module declaration.
 ///
 /// E.g., `extern { .. }` or `extern "C" { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ForeignMod {
     /// Span of the `extern` keyword.
     pub extern_span: Span,
@@ -3265,12 +3274,13 @@ pub struct ForeignMod {
     pub items: ThinVec<P<ForeignItem>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct EnumDef {
     pub variants: ThinVec<Variant>,
 }
+
 /// Enum variant.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Variant {
     /// Attributes of the variant.
     pub attrs: AttrVec,
@@ -3292,7 +3302,7 @@ pub struct Variant {
 }
 
 /// Part of `use` item to the right of its prefix.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum UseTreeKind {
     /// `use prefix` or `use prefix as rename`
     Simple(Option<Ident>),
@@ -3311,7 +3321,7 @@ pub enum UseTreeKind {
 
 /// A tree of paths sharing common prefixes.
 /// Used in `use` items both at top-level and inside of braces in import groups.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct UseTree {
     pub prefix: Path,
     pub kind: UseTreeKind,
@@ -3333,7 +3343,7 @@ impl UseTree {
 /// Distinguishes between `Attribute`s that decorate items and Attributes that
 /// are contained as statements within items. These two cases need to be
 /// distinguished for pretty-printing.
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic, Walkable)]
 pub enum AttrStyle {
     Outer,
     Inner,
@@ -3343,7 +3353,7 @@ pub enum AttrStyle {
 pub type AttrVec = ThinVec<Attribute>;
 
 /// A syntax-level representation of an attribute.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Attribute {
     pub kind: AttrKind,
     pub id: AttrId,
@@ -3353,7 +3363,7 @@ pub struct Attribute {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AttrKind {
     /// A normal attribute.
     Normal(P<NormalAttr>),
@@ -3364,7 +3374,7 @@ pub enum AttrKind {
     DocComment(CommentKind, Symbol),
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct NormalAttr {
     pub item: AttrItem,
     // Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`.
@@ -3385,7 +3395,7 @@ impl NormalAttr {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AttrItem {
     pub unsafety: Safety,
     pub path: Path,
@@ -3411,20 +3421,20 @@ impl AttrItem {
 /// that the `ref_id` is for. The `impl_id` maps to the "self type" of this impl.
 /// If this impl is an `ItemKind::Impl`, the `impl_id` is redundant (it could be the
 /// same as the impl's `NodeId`).
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TraitRef {
     pub path: Path,
     pub ref_id: NodeId,
 }
 
 /// Whether enclosing parentheses are present or not.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum Parens {
     Yes,
     No,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PolyTraitRef {
     /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: ThinVec<GenericParam>,
@@ -3460,14 +3470,14 @@ impl PolyTraitRef {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Visibility {
     pub kind: VisibilityKind,
     pub span: Span,
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum VisibilityKind {
     Public,
     Restricted { path: P<Path>, id: NodeId, shorthand: bool },
@@ -3483,7 +3493,7 @@ impl VisibilityKind {
 /// Field definition in a struct, variant or union.
 ///
 /// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FieldDef {
     pub attrs: AttrVec,
     pub id: NodeId,
@@ -3498,14 +3508,14 @@ pub struct FieldDef {
 }
 
 /// Was parsing recovery performed?
-#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
+#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum Recovered {
     No,
     Yes(ErrorGuaranteed),
 }
 
 /// Fields and constructor ids of enum variants and structs.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum VariantData {
     /// Struct variant.
     ///
@@ -3591,7 +3601,7 @@ impl Item {
 }
 
 /// `extern` qualifier on a function item or function type.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub enum Extern {
     /// No explicit extern keyword was used.
     ///
@@ -3622,7 +3632,7 @@ impl Extern {
 ///
 /// All the information between the visibility and the name of the function is
 /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub struct FnHeader {
     /// Whether this is `unsafe`, or has a default safety.
     pub safety: Safety,
@@ -3688,14 +3698,16 @@ impl Default for FnHeader {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Trait {
     pub constness: Const,
     pub safety: Safety,
     pub is_auto: IsAuto,
     pub ident: Ident,
     pub generics: Generics,
+    #[visitable(extra = BoundKind::SuperTraits)]
     pub bounds: GenericBounds,
+    #[visitable(extra = AssocCtxt::Trait)]
     pub items: ThinVec<P<AssocItem>>,
 }
 
@@ -3717,14 +3729,14 @@ pub struct Trait {
 /// ```
 ///
 /// If there is no where clause, then this is `false` with `DUMMY_SP`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct TyAliasWhereClause {
     pub has_where_token: bool,
     pub span: Span,
 }
 
 /// The span information for the two where clauses on a `TyAlias`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct TyAliasWhereClauses {
     /// Before the equals sign.
     pub before: TyAliasWhereClause,
@@ -3736,12 +3748,13 @@ pub struct TyAliasWhereClauses {
     pub split: usize,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TyAlias {
     pub defaultness: Defaultness,
     pub ident: Ident,
     pub generics: Generics,
     pub where_clauses: TyAliasWhereClauses,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
     pub ty: Option<P<Ty>>,
 }
@@ -3759,7 +3772,7 @@ pub struct Impl {
     pub items: ThinVec<P<AssocItem>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct FnContract {
     pub requires: Option<P<Expr>>,
     pub ensures: Option<P<Expr>>,
@@ -3776,7 +3789,7 @@ pub struct Fn {
     pub body: Option<P<Block>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Delegation {
     /// Path resolution id.
     pub id: NodeId,
@@ -3789,7 +3802,7 @@ pub struct Delegation {
     pub from_glob: bool,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct DelegationMac {
     pub qself: Option<P<QSelf>>,
     pub prefix: Path,
@@ -3798,7 +3811,7 @@ pub struct DelegationMac {
     pub body: Option<P<Block>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct StaticItem {
     pub ident: Ident,
     pub ty: P<Ty>,
@@ -3808,7 +3821,7 @@ pub struct StaticItem {
     pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ConstItem {
     pub defaultness: Defaultness,
     pub ident: Ident,
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
index 28d260419c5..c2a1de60a98 100644
--- a/compiler/rustc_ast/src/format.rs
+++ b/compiler/rustc_ast/src/format.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::FxHashMap;
-use rustc_macros::{Decodable, Encodable};
+use rustc_macros::{Decodable, Encodable, Walkable};
 use rustc_span::{Ident, Span, Symbol};
 
 use crate::Expr;
@@ -41,7 +41,7 @@ use crate::token::LitKind;
 /// Basically the "AST" for a complete `format_args!()`.
 ///
 /// E.g., `format_args!("hello {name}");`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArgs {
     pub span: Span,
     pub template: Vec<FormatArgsPiece>,
@@ -63,7 +63,7 @@ pub struct FormatArgs {
 /// A piece of a format template string.
 ///
 /// E.g. "hello" or "{name}".
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FormatArgsPiece {
     Literal(Symbol),
     Placeholder(FormatPlaceholder),
@@ -73,7 +73,7 @@ pub enum FormatArgsPiece {
 ///
 /// E.g. `1, 2, name="ferris", n=3`,
 /// but also implicit captured arguments like `x` in `format_args!("{x}")`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArguments {
     arguments: Vec<FormatArgument>,
     num_unnamed_args: usize,
@@ -144,13 +144,13 @@ impl FormatArguments {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArgument {
     pub kind: FormatArgumentKind,
     pub expr: P<Expr>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FormatArgumentKind {
     /// `format_args(…, arg)`
     Normal,
@@ -170,24 +170,28 @@ impl FormatArgumentKind {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub struct FormatPlaceholder {
     /// Index into [`FormatArgs::arguments`].
     pub argument: FormatArgPosition,
     /// The span inside the format string for the full `{…}` placeholder.
     pub span: Option<Span>,
     /// `{}`, `{:?}`, or `{:x}`, etc.
+    #[visitable(ignore)]
     pub format_trait: FormatTrait,
     /// `{}` or `{:.5}` or `{:-^20}`, etc.
+    #[visitable(ignore)]
     pub format_options: FormatOptions,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub struct FormatArgPosition {
     /// Which argument this position refers to (Ok),
     /// or would've referred to if it existed (Err).
+    #[visitable(ignore)]
     pub index: Result<usize, usize>,
     /// What kind of position this is. See [`FormatArgPositionKind`].
+    #[visitable(ignore)]
     pub kind: FormatArgPositionKind,
     /// The span of the name or number.
     pub span: Option<Span>,
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 3eae19f4daa..06708e2e703 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -12,14 +12,14 @@ use std::panic;
 
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span};
+use rustc_span::{Ident, Span, Symbol};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 
 use crate::ast::*;
 use crate::ptr::P;
 use crate::tokenstream::*;
-use crate::visit::{AssocCtxt, BoundKind, FnCtxt, VisitorResult, try_visit, visit_opt, walk_list};
+use crate::visit::{AssocCtxt, BoundKind, FnCtxt, LifetimeCtxt, VisitorResult, try_visit};
 
 mod sealed {
     use rustc_ast_ir::visit::VisitorResult;
@@ -36,11 +36,249 @@ mod sealed {
 
 use sealed::MutVisitorResult;
 
+pub(crate) trait MutVisitable<V: MutVisitor> {
+    type Extra: Copy;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra);
+}
+
+impl<V: MutVisitor, T: ?Sized> MutVisitable<V> for P<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        (**self).visit_mut(visitor, extra)
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Option<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        if let Some(this) = self {
+            this.visit_mut(visitor, extra)
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Spanned<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        let Spanned { span, node } = self;
+        span.visit_mut(visitor, ());
+        node.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for [T]
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        for item in self {
+            item.visit_mut(visitor, extra);
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Vec<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        for item in self {
+            item.visit_mut(visitor, extra);
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for (T,)
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2> MutVisitable<V> for (T1, T2)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2, T3> MutVisitable<V> for (T1, T2, T3)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+    T3: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+        self.2.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2, T3, T4> MutVisitable<V> for (T1, T2, T3, T4)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+    T3: MutVisitable<V, Extra = ()>,
+    T4: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+        self.2.visit_mut(visitor, extra);
+        self.3.visit_mut(visitor, extra);
+    }
+}
+
+pub trait MutWalkable<V: MutVisitor> {
+    fn walk_mut(&mut self, visitor: &mut V);
+}
+
+macro_rules! visit_visitable {
+    (mut $visitor:expr, $($expr:expr),* $(,)?) => {{
+        $(MutVisitable::visit_mut($expr, $visitor, ());)*
+    }};
+}
+
+macro_rules! visit_visitable_with {
+    (mut $visitor:expr, $expr:expr, $extra:expr $(,)?) => {
+        MutVisitable::visit_mut($expr, $visitor, $extra)
+    };
+}
+
+macro_rules! walk_walkable {
+    ($visitor:expr, $expr:expr, mut) => {
+        MutWalkable::walk_mut($expr, $visitor)
+    };
+}
+
+macro_rules! impl_visitable {
+    (|&mut $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident,
+      $extra:ident: $extra_ty:ty| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$vis_ty: MutVisitor> MutVisitable<$vis_ty> for $self_ty {
+            type Extra = $extra_ty;
+            fn visit_mut(&mut $self, $vis: &mut $vis_ty, $extra: Self::Extra) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_walkable {
+    ($(<$K:ident: $Kb:ident>)? |&mut $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$($K: $Kb,)? $vis_ty: MutVisitor> MutWalkable<$vis_ty> for $self_ty {
+            fn walk_mut(&mut $self, $vis: &mut $vis_ty) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_visitable_noop {
+    (<mut> $($ty:ty,)*) => {
+        $(
+            impl_visitable!(|&mut self: $ty, _vis: &mut V, _extra: ()| {});
+        )*
+    };
+}
+
+macro_rules! impl_visitable_list {
+    (<mut> $($ty:ty,)*) => {
+        $(impl<V: MutVisitor, T> MutVisitable<V> for $ty
+        where
+            for<'a> &'a mut $ty: IntoIterator<Item = &'a mut T>,
+            T: MutVisitable<V>,
+        {
+            type Extra = <T as MutVisitable<V>>::Extra;
+
+            #[inline]
+            fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+                for i in self {
+                    i.visit_mut(visitor, extra);
+                }
+            }
+        })*
+    }
+}
+
+macro_rules! impl_visitable_direct {
+    (<mut> $($ty:ty,)*) => {
+        $(impl_visitable!(
+            |&mut self: $ty, visitor: &mut V, _extra: ()| {
+                MutWalkable::walk_mut(self, visitor)
+            }
+        );)*
+    }
+}
+
+macro_rules! impl_visitable_calling_walkable {
+    (<mut>
+        $( fn $method:ident($ty:ty $(, $extra_name:ident: $extra_ty:ty)?); )*
+    ) => {
+        $(fn $method(&mut self, node: &mut $ty $(, $extra_name:$extra_ty)?) {
+            impl_visitable!(|&mut self: $ty, visitor: &mut V, extra: ($($extra_ty)?)| {
+                let ($($extra_name)?) = extra;
+                visitor.$method(self $(, $extra_name)?);
+            });
+            walk_walkable!(self, node, mut)
+        })*
+    }
+}
+
+macro_rules! define_named_walk {
+    ((mut) $Visitor:ident
+        $( pub fn $method:ident($ty:ty); )*
+    ) => {
+        $(pub fn $method<V: $Visitor>(visitor: &mut V, node: &mut $ty) {
+            walk_walkable!(visitor, node, mut)
+        })*
+    };
+}
+
 super::common_visitor_and_walkers!((mut) MutVisitor);
 
 macro_rules! generate_flat_map_visitor_fns {
     ($($name:ident, $Ty:ty, $flat_map_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
         $(
+            #[allow(unused_parens)]
+            impl<V: MutVisitor> MutVisitable<V> for ThinVec<$Ty> {
+                type Extra = ($($ParamTy),*);
+
+                #[inline]
+                fn visit_mut(
+                    &mut self,
+                    visitor: &mut V,
+                    ($($param),*): Self::Extra,
+                ) -> V::Result {
+                    $name(visitor, self $(, $param)*)
+                }
+            }
+
             fn $name<V: MutVisitor>(
                 vis: &mut V,
                 values: &mut ThinVec<$Ty>,
@@ -78,15 +316,6 @@ pub fn walk_flat_map_pat_field<T: MutVisitor>(
     smallvec![fp]
 }
 
-fn visit_nested_use_tree<V: MutVisitor>(
-    vis: &mut V,
-    nested_tree: &mut UseTree,
-    nested_id: &mut NodeId,
-) {
-    vis.visit_id(nested_id);
-    vis.visit_use_tree(nested_tree);
-}
-
 macro_rules! generate_walk_flat_map_fns {
     ($($fn_name:ident($Ty:ty$(,$extra_name:ident: $ExtraTy:ty)*) => $visit_fn_name:ident;)+) => {$(
         pub fn $fn_name<V: MutVisitor>(vis: &mut V, mut value: $Ty$(,$extra_name: $ExtraTy)*) -> SmallVec<[$Ty; 1]> {
@@ -109,14 +338,6 @@ generate_walk_flat_map_fns! {
     walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
 }
 
-fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWhereClauses) {
-    let TyAliasWhereClauses { before, after, split: _ } = tawcs;
-    let TyAliasWhereClause { has_where_token: _, span: span_before } = before;
-    let TyAliasWhereClause { has_where_token: _, span: span_after } = after;
-    vis.visit_span(span_before);
-    vis.visit_span(span_after);
-}
-
 pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
     vis.visit_expr(&mut e);
     Some(e)
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index c60185cdde0..e55399adfb8 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -20,7 +20,7 @@ use std::{cmp, fmt, iter, mem};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync;
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable};
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
 use thin_vec::ThinVec;
@@ -977,7 +977,7 @@ impl TokenCursor {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub struct DelimSpan {
     pub open: Span,
     pub close: Span,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index a344f23c345..ab15cb28fa1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -16,7 +16,7 @@
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span};
+use rustc_span::{Ident, Span, Symbol};
 use thin_vec::ThinVec;
 
 use crate::ast::*;
@@ -75,6 +75,241 @@ pub enum LifetimeCtxt {
     GenericArg,
 }
 
+pub(crate) trait Visitable<'a, V: Visitor<'a>> {
+    type Extra: Copy;
+
+    #[must_use]
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result;
+}
+
+impl<'a, V: Visitor<'a>, T: ?Sized> Visitable<'a, V> for P<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        (**self).visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Option<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        if let Some(this) = self {
+            try_visit!(this.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Spanned<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        let Spanned { span: _, node } = self;
+        node.visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for [T]
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        for item in self {
+            try_visit!(item.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Vec<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        for item in self {
+            try_visit!(item.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for (T,)
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        self.0.visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2> Visitable<'a, V> for (T1, T2)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2, T3> Visitable<'a, V> for (T1, T2, T3)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+    T3: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        try_visit!(self.2.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2, T3, T4> Visitable<'a, V> for (T1, T2, T3, T4)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+    T3: Visitable<'a, V, Extra = ()>,
+    T4: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        try_visit!(self.2.visit(visitor, extra));
+        try_visit!(self.3.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+pub(crate) trait Walkable<'a, V: Visitor<'a>> {
+    #[must_use]
+    fn walk_ref(&'a self, visitor: &mut V) -> V::Result;
+}
+
+macro_rules! visit_visitable {
+    ($visitor:expr, $($expr:expr),* $(,)?) => {{
+        $(try_visit!(Visitable::visit($expr, $visitor, ()));)*
+    }};
+}
+
+macro_rules! visit_visitable_with {
+    ($visitor:expr, $expr:expr, $extra:expr $(,)?) => {
+        try_visit!(Visitable::visit($expr, $visitor, $extra))
+    };
+}
+
+macro_rules! walk_walkable {
+    ($visitor:expr, $expr:expr, ) => {
+        Walkable::walk_ref($expr, $visitor)
+    };
+}
+
+macro_rules! impl_visitable {
+    (|&$lt:lifetime $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident,
+      $extra:ident: $extra_ty:ty| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$lt, $vis_ty: Visitor<$lt>> Visitable<$lt, $vis_ty> for $self_ty {
+            type Extra = $extra_ty;
+            fn visit(&$lt $self, $vis: &mut $vis_ty, $extra: Self::Extra) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_walkable {
+    ($(<$K:ident: $Kb:ident>)? |&$lt:lifetime $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$($K: $Kb,)? $lt, $vis_ty: Visitor<$lt>> Walkable<$lt, $vis_ty> for $self_ty {
+            fn walk_ref(&$lt $self, $vis: &mut $vis_ty) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_visitable_noop {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(
+            impl_visitable!(|&$lt self: $ty, _vis: &mut V, _extra: ()| {
+                V::Result::output()
+            });
+        )*
+    };
+}
+
+macro_rules! impl_visitable_list {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(impl<$lt, V: Visitor<$lt>, T> Visitable<$lt, V> for $ty
+        where
+            &$lt $ty: IntoIterator<Item = &$lt T>,
+            T: $lt + Visitable<$lt, V>,
+        {
+            type Extra = <T as Visitable<$lt, V>>::Extra;
+
+            #[inline]
+            fn visit(&$lt self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+                for i in self {
+                    try_visit!(i.visit(visitor, extra));
+                }
+                V::Result::output()
+            }
+        })*
+    };
+}
+
+macro_rules! impl_visitable_direct {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(impl_visitable!(
+            |&$lt self: $ty, visitor: &mut V, _extra: ()| {
+                Walkable::walk_ref(self, visitor)
+            }
+        );)*
+    };
+}
+
+macro_rules! impl_visitable_calling_walkable {
+    (<$lt:lifetime>
+        $( fn $method:ident($ty:ty $(, $extra_name:ident: $extra_ty:ty)?); )*
+    ) => {
+        $(fn $method(&mut self, node: &$lt $ty $(, $extra_name:$extra_ty)?) -> Self::Result {
+            impl_visitable!(|&$lt self: $ty, visitor: &mut V, extra: ($($extra_ty)?)| {
+                let ($($extra_name)?) = extra;
+                visitor.$method(self $(, $extra_name)?)
+            });
+            walk_walkable!(self, node, )
+        })*
+    };
+}
+
+macro_rules! define_named_walk {
+    ($Visitor:ident<$lt:lifetime>
+        $( pub fn $method:ident($ty:ty); )*
+    ) => {
+        $(pub fn $method<$lt, V: $Visitor<$lt>>(visitor: &mut V, node: &$lt $ty) -> V::Result {
+            walk_walkable!(visitor, node,)
+        })*
+    };
+}
+
 #[macro_export]
 macro_rules! common_visitor_and_walkers {
     ($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => {
@@ -120,6 +355,139 @@ macro_rules! common_visitor_and_walkers {
             }
         }
 
+        // This macro generates `impl Visitable` and `impl MutVisitable` that do nothing.
+        impl_visitable_noop!(<$($lt)? $($mut)?>
+            AttrId,
+            bool,
+            rustc_span::ByteSymbol,
+            char,
+            crate::token::CommentKind,
+            crate::token::Delimiter,
+            crate::token::Lit,
+            crate::token::LitKind,
+            crate::tokenstream::LazyAttrTokenStream,
+            crate::tokenstream::TokenStream,
+            Movability,
+            Mutability,
+            Result<(), rustc_span::ErrorGuaranteed>,
+            rustc_data_structures::fx::FxHashMap<Symbol, usize>,
+            rustc_span::ErrorGuaranteed,
+            std::borrow::Cow<'_, str>,
+            Symbol,
+            u8,
+            usize,
+        );
+        // `Span` is only a no-op for the non-mutable visitor.
+        $(impl_visitable_noop!(<$lt> Span,);)?
+
+        // This macro generates `impl Visitable` and `impl MutVisitable` that simply iterate over
+        // their contents. We do not use a generic impl for `ThinVec` because we want to allow
+        // custom visits for the `MutVisitor`.
+        impl_visitable_list!(<$($lt)? $($mut)?>
+            ThinVec<AngleBracketedArg>,
+            ThinVec<Attribute>,
+            ThinVec<(Ident, Option<Ident>)>,
+            ThinVec<(NodeId, Path)>,
+            ThinVec<PathSegment>,
+            ThinVec<PreciseCapturingArg>,
+            ThinVec<P<Pat>>,
+            ThinVec<P<Ty>>,
+            ThinVec<P<TyPat>>,
+        );
+
+        // This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
+        // or `MutWalkable`. By default, all types that do not have a custom visit method in the
+        // visitor should appear here.
+        impl_visitable_direct!(<$($lt)? $($mut)?>
+            AngleBracketedArg,
+            AngleBracketedArgs,
+            AsmMacro,
+            AssignOpKind,
+            AssocItemConstraintKind,
+            AttrArgs,
+            AttrItem,
+            AttrKind,
+            AttrStyle,
+            FnPtrTy,
+            BindingMode,
+            GenBlockKind,
+            RangeLimits,
+            UnsafeBinderCastKind,
+            BinOpKind,
+            BlockCheckMode,
+            BorrowKind,
+            BoundAsyncness,
+            BoundConstness,
+            BoundPolarity,
+            ByRef,
+            Closure,
+            Const,
+            ConstItem,
+            Defaultness,
+            Delegation,
+            DelegationMac,
+            DelimArgs,
+            DelimSpan,
+            EnumDef,
+            Extern,
+            ForLoopKind,
+            FormatArgPosition,
+            FormatArgsPiece,
+            FormatArgument,
+            FormatArgumentKind,
+            FormatArguments,
+            FormatPlaceholder,
+            GenericParamKind,
+            Impl,
+            ImplPolarity,
+            Inline,
+            InlineAsmOperand,
+            InlineAsmRegOrRegClass,
+            InlineAsmTemplatePiece,
+            IsAuto,
+            LocalKind,
+            MacCallStmt,
+            MacStmtStyle,
+            MatchKind,
+            MethodCall,
+            ModKind,
+            ModSpans,
+            MutTy,
+            NormalAttr,
+            Parens,
+            ParenthesizedArgs,
+            PatFieldsRest,
+            PatKind,
+            RangeEnd,
+            RangeSyntax,
+            Recovered,
+            Safety,
+            StaticItem,
+            StrLit,
+            StrStyle,
+            StructExpr,
+            StructRest,
+            Term,
+            Trait,
+            TraitBoundModifiers,
+            TraitObjectSyntax,
+            TyAlias,
+            TyAliasWhereClause,
+            TyAliasWhereClauses,
+            TyKind,
+            TyPatKind,
+            UnOp,
+            UnsafeBinderTy,
+            UnsafeSource,
+            UseTreeKind,
+            VisibilityKind,
+            WhereBoundPredicate,
+            WhereClause,
+            WhereEqPredicate,
+            WhereRegionPredicate,
+            YieldKind,
+        );
+
         /// Each method of this trait is a hook to be potentially
         /// overridden. Each method's default implementation recursively visits
         /// the substructure of the input via the corresponding `walk` method;
@@ -169,47 +537,82 @@ macro_rules! common_visitor_and_walkers {
             // field access version will continue working and it would be easy to
             // forget to add handling for it.
             fn visit_ident(&mut self, Ident { name: _, span }: &$($lt)? $($mut)? Ident) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: Ident, visitor: &mut V, _extra: ()| {
+                    visitor.visit_ident(self)
+                });
                 visit_span(self, span)
             }
 
-            fn visit_foreign_mod(&mut self, nm: &$($lt)? $($mut)? ForeignMod) -> Self::Result {
-                walk_foreign_mod(self, nm)
-            }
-
-            fn visit_foreign_item(&mut self, i: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
-                walk_item(self, i)
-            }
-
-            fn visit_item(&mut self, i: &$($lt)? $($mut)? Item) -> Self::Result {
-                walk_item(self, i)
-            }
-
-            fn visit_local(&mut self, l: &$($lt)? $($mut)? Local) -> Self::Result {
-                walk_local(self, l)
-            }
-
-            fn visit_block(&mut self, b: &$($lt)? $($mut)? Block) -> Self::Result {
-                walk_block(self, b)
-            }
-
-            fn visit_param(&mut self, param: &$($lt)? $($mut)? Param) -> Self::Result {
-                walk_param(self, param)
-            }
-
-            fn visit_arm(&mut self, a: &$($lt)? $($mut)? Arm) -> Self::Result {
-                walk_arm(self, a)
-            }
-
-            fn visit_pat(&mut self, p: &$($lt)? $($mut)? Pat) -> Self::Result {
-                walk_pat(self, p)
-            }
-
-            fn visit_anon_const(&mut self, c: &$($lt)? $($mut)? AnonConst) -> Self::Result {
-                walk_anon_const(self, c)
-            }
-
-            fn visit_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
-                walk_expr(self, ex)
+            // This macro defines a custom visit method for each listed type.
+            // It implements `impl Visitable` and `impl MutVisitable` to call those methods on the
+            // visitor.
+            impl_visitable_calling_walkable!(<$($lt)? $($mut)?>
+                fn visit_anon_const(AnonConst);
+                fn visit_arm(Arm);
+                //fn visit_assoc_item(AssocItem, _ctxt: AssocCtxt);
+                fn visit_assoc_item_constraint(AssocItemConstraint);
+                fn visit_attribute(Attribute);
+                fn visit_block(Block);
+                //fn visit_nested_use_tree((UseTree, NodeId));
+                fn visit_capture_by(CaptureBy);
+                fn visit_closure_binder(ClosureBinder);
+                fn visit_contract(FnContract);
+                fn visit_coroutine_kind(CoroutineKind);
+                fn visit_crate(Crate);
+                fn visit_expr(Expr);
+                fn visit_expr_field(ExprField);
+                fn visit_field_def(FieldDef);
+                fn visit_fn_decl(FnDecl);
+                fn visit_fn_header(FnHeader);
+                fn visit_fn_ret_ty(FnRetTy);
+                //fn visit_foreign_item(ForeignItem);
+                fn visit_foreign_mod(ForeignMod);
+                fn visit_format_args(FormatArgs);
+                fn visit_generic_arg(GenericArg);
+                fn visit_generic_args(GenericArgs);
+                fn visit_generic_param(GenericParam);
+                fn visit_generics(Generics);
+                fn visit_inline_asm(InlineAsm);
+                fn visit_inline_asm_sym(InlineAsmSym);
+                //fn visit_item(Item);
+                fn visit_label(Label);
+                fn visit_lifetime(Lifetime, _ctxt: LifetimeCtxt);
+                fn visit_local(Local);
+                fn visit_mac_call(MacCall);
+                fn visit_macro_def(MacroDef);
+                fn visit_param_bound(GenericBound, _ctxt: BoundKind);
+                fn visit_param(Param);
+                fn visit_pat_field(PatField);
+                fn visit_path(Path);
+                fn visit_path_segment(PathSegment);
+                fn visit_pat(Pat);
+                fn visit_poly_trait_ref(PolyTraitRef);
+                fn visit_precise_capturing_arg(PreciseCapturingArg);
+                fn visit_qself(QSelf);
+                fn visit_trait_ref(TraitRef);
+                fn visit_ty_pat(TyPat);
+                fn visit_ty(Ty);
+                fn visit_use_tree(UseTree);
+                fn visit_variant_data(VariantData);
+                fn visit_variant(Variant);
+                fn visit_vis(Visibility);
+                fn visit_where_predicate_kind(WherePredicateKind);
+                fn visit_where_predicate(WherePredicate);
+            );
+
+            // We want `Visitor` to take the `NodeId` by value.
+            fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
+                $(impl_visitable!(
+                    |&$lt self: NodeId, visitor: &mut V, _extra: ()| {
+                        visitor.visit_id(*self)
+                    }
+                );)?
+                $(impl_visitable!(
+                    |&$mut self: NodeId, visitor: &mut V, _extra: ()| {
+                        visitor.visit_id(self)
+                    }
+                );)?
+                Self::Result::output()
             }
 
             /// This method is a hack to workaround unstable of `stmt_expr_attributes`.
@@ -218,34 +621,25 @@ macro_rules! common_visitor_and_walkers {
                 self.visit_expr(ex)
             }
 
-            fn visit_ty(&mut self, t: &$($lt)? $($mut)? Ty) -> Self::Result {
-                walk_ty(self, t)
-            }
-
-            fn visit_ty_pat(&mut self, t: &$($lt)? $($mut)? TyPat) -> Self::Result {
-                walk_ty_pat(self, t)
-            }
-
-            fn visit_generic_param(&mut self, param: &$($lt)? $($mut)? GenericParam) -> Self::Result {
-                walk_generic_param(self, param)
-            }
-
-            fn visit_generics(&mut self, g: &$($lt)? $($mut)? Generics) -> Self::Result {
-                walk_generics(self, g)
-            }
-            fn visit_closure_binder(&mut self, b: &$($lt)? $($mut)? ClosureBinder) -> Self::Result {
-                walk_closure_binder(self, b)
-            }
-            fn visit_contract(&mut self, c: &$($lt)? $($mut)? FnContract) -> Self::Result {
-                walk_contract(self, c)
+            fn visit_item(&mut self, item: &$($lt)? $($mut)? Item) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: Item, vis: &mut V, _extra: ()| {
+                    vis.visit_item(self)
+                });
+                walk_item(self, item)
             }
 
-            fn visit_where_predicate(&mut self, p: &$($lt)? $($mut)? WherePredicate) -> Self::Result {
-                walk_where_predicate(self, p)
+            fn visit_foreign_item(&mut self, item: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: ForeignItem, vis: &mut V, _extra: ()| {
+                    vis.visit_foreign_item(self)
+                });
+                walk_item(self, item)
             }
 
-            fn visit_where_predicate_kind(&mut self, k: &$($lt)? $($mut)? WherePredicateKind) -> Self::Result {
-                walk_where_predicate_kind(self, k)
+            fn visit_assoc_item(&mut self, item: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: AssocItem, vis: &mut V, ctxt: AssocCtxt| {
+                    vis.visit_assoc_item(self, ctxt)
+                });
+                walk_assoc_item(self, item, ctxt)
             }
 
             // for `MutVisitor`: `Span` and `NodeId` are mutated at the caller site.
@@ -258,141 +652,6 @@ macro_rules! common_visitor_and_walkers {
                 walk_fn(self, fk)
             }
 
-            fn visit_assoc_item(&mut self, i: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
-                walk_assoc_item(self, i, ctxt)
-            }
-
-            fn visit_trait_ref(&mut self, t: &$($lt)? $($mut)? TraitRef) -> Self::Result {
-                walk_trait_ref(self, t)
-            }
-
-            fn visit_param_bound(&mut self, bounds: &$($lt)? $($mut)? GenericBound, _ctxt: BoundKind) -> Self::Result {
-                walk_param_bound(self, bounds)
-            }
-
-            fn visit_precise_capturing_arg(&mut self, arg: &$($lt)? $($mut)? PreciseCapturingArg) -> Self::Result {
-                walk_precise_capturing_arg(self, arg)
-            }
-
-            fn visit_poly_trait_ref(&mut self, t: &$($lt)? $($mut)? PolyTraitRef) -> Self::Result {
-                walk_poly_trait_ref(self, t)
-            }
-
-            fn visit_variant_data(&mut self, s: &$($lt)? $($mut)? VariantData) -> Self::Result {
-                walk_variant_data(self, s)
-            }
-
-            fn visit_field_def(&mut self, s: &$($lt)? $($mut)? FieldDef) -> Self::Result {
-                walk_field_def(self, s)
-            }
-
-            fn visit_variant(&mut self, v: &$($lt)? $($mut)? Variant) -> Self::Result {
-                walk_variant(self, v)
-            }
-
-            fn visit_label(&mut self, label: &$($lt)? $($mut)? Label) -> Self::Result {
-                walk_label(self, label)
-            }
-
-            fn visit_lifetime(&mut self, lifetime: &$($lt)? $($mut)? Lifetime, $(${ignore($lt)} _: LifetimeCtxt )?) -> Self::Result {
-                walk_lifetime(self, lifetime)
-            }
-
-            fn visit_mac_call(&mut self, mac: &$($lt)? $($mut)? MacCall) -> Self::Result {
-                walk_mac(self, mac)
-            }
-
-            fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
-                Self::Result::output()
-            }
-
-            fn visit_macro_def(&mut self, macro_def: &$($lt)? $($mut)? MacroDef) -> Self::Result {
-                walk_macro_def(self, macro_def)
-            }
-
-            fn visit_path(&mut self, path: &$($lt)? $($mut)? Path) -> Self::Result {
-                walk_path(self, path)
-            }
-
-            fn visit_use_tree(&mut self, use_tree: &$($lt)? $($mut)? UseTree) -> Self::Result {
-                walk_use_tree(self, use_tree)
-            }
-
-            fn visit_path_segment(&mut self, path_segment: &$($lt)? $($mut)? PathSegment) -> Self::Result {
-                walk_path_segment(self, path_segment)
-            }
-
-            fn visit_generic_args(&mut self, generic_args: &$($lt)? $($mut)? GenericArgs) -> Self::Result {
-                walk_generic_args(self, generic_args)
-            }
-
-            fn visit_generic_arg(&mut self, generic_arg: &$($lt)? $($mut)? GenericArg) -> Self::Result {
-                walk_generic_arg(self, generic_arg)
-            }
-
-            fn visit_assoc_item_constraint(
-                &mut self,
-                constraint: &$($lt)? $($mut)? AssocItemConstraint,
-            ) -> Self::Result {
-                walk_assoc_item_constraint(self, constraint)
-            }
-
-            fn visit_attribute(&mut self, attr: &$($lt)? $($mut)? Attribute) -> Self::Result {
-                walk_attribute(self, attr)
-            }
-
-            fn visit_vis(&mut self, vis: &$($lt)? $($mut)? Visibility) -> Self::Result {
-                walk_vis(self, vis)
-            }
-
-            fn visit_fn_ret_ty(&mut self, ret_ty: &$($lt)? $($mut)? FnRetTy) -> Self::Result {
-                walk_fn_ret_ty(self, ret_ty)
-            }
-
-            fn visit_fn_header(&mut self, header: &$($lt)? $($mut)? FnHeader) -> Self::Result {
-                walk_fn_header(self, header)
-            }
-
-            fn visit_expr_field(&mut self, f: &$($lt)? $($mut)? ExprField) -> Self::Result {
-                walk_expr_field(self, f)
-            }
-
-            fn visit_pat_field(&mut self, fp: &$($lt)? $($mut)? PatField) -> Self::Result {
-                walk_pat_field(self, fp)
-            }
-
-            fn visit_crate(&mut self, krate: &$($lt)? $($mut)? Crate) -> Self::Result {
-                walk_crate(self, krate)
-            }
-
-            fn visit_inline_asm(&mut self, asm: &$($lt)? $($mut)? InlineAsm) -> Self::Result {
-                walk_inline_asm(self, asm)
-            }
-
-            fn visit_format_args(&mut self, fmt: &$($lt)? $($mut)? FormatArgs) -> Self::Result {
-                walk_format_args(self, fmt)
-            }
-
-            fn visit_inline_asm_sym(&mut self, sym: &$($lt)? $($mut)? InlineAsmSym) -> Self::Result {
-                walk_inline_asm_sym(self, sym)
-            }
-
-            fn visit_capture_by(&mut self, capture_by: &$($lt)? $($mut)? CaptureBy) -> Self::Result {
-                walk_capture_by(self, capture_by)
-            }
-
-            fn visit_coroutine_kind(&mut self, coroutine_kind: &$($lt)? $($mut)? CoroutineKind) -> Self::Result {
-                walk_coroutine_kind(self, coroutine_kind)
-            }
-
-            fn visit_fn_decl(&mut self, fn_decl: &$($lt)? $($mut)? FnDecl) -> Self::Result {
-                walk_fn_decl(self, fn_decl)
-            }
-
-            fn visit_qself(&mut self, qs: &$($lt)? $($mut)? Option<P<QSelf>>) -> Self::Result {
-                walk_qself(self, qs)
-            }
-
             // (non-mut) `Visitor`-only methods
             $(
                 fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result {
@@ -407,6 +666,16 @@ macro_rules! common_visitor_and_walkers {
 
             // `MutVisitor`-only methods
             $(
+                // Span visiting is no longer used, but we keep it for now,
+                // in case it's needed for something like #127241.
+                #[inline]
+                fn visit_span(&mut self, _sp: &$mut Span) {
+                    impl_visitable!(|&mut self: Span, visitor: &mut V, _extra: ()| {
+                        visitor.visit_span(self)
+                    });
+                    // Do nothing.
+                }
+
                 fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
                     walk_flat_map_foreign_item(self, ni)
                 }
@@ -462,12 +731,6 @@ macro_rules! common_visitor_and_walkers {
                     walk_flat_map_where_predicate(self, where_predicate)
                 }
 
-                // Span visiting is no longer used, but we keep it for now,
-                // in case it's needed for something like #127241.
-                fn visit_span(&mut self, _sp: &$mut Span) {
-                    // Do nothing.
-                }
-
                 fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
                     walk_flat_map_pat_field(self, fp)
                 }
@@ -492,148 +755,45 @@ macro_rules! common_visitor_and_walkers {
             #[inline]
         )?
         fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, span: &$($lt)? $($mut)? Span) -> V::Result {
-            $(
-                ${ignore($mut)}
-                vis.visit_span(span);
-            )?
-            V::Result::output()
-        }
-
-        /// helper since `Visitor` wants `NodeId` but `MutVisitor` wants `&mut NodeId`
-        $(${ignore($lt)}
-            #[expect(rustc::pass_by_value)]
-        )?
-        #[inline]
-        fn visit_id<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, id: &$($lt)? $($mut)? NodeId) -> V::Result {
-            // deref `&NodeId` into `NodeId` only for `Visitor`
-            vis.visit_id( $(${ignore($lt)} * )? id)
-        }
-
-        // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier
-        fn visit_safety<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, safety: &$($lt)? $($mut)? Safety) -> V::Result {
-            match safety {
-                Safety::Unsafe(span) => visit_span(vis, span),
-                Safety::Safe(span) => visit_span(vis, span),
-                Safety::Default => { V::Result::output() }
-            }
-        }
-
-        fn visit_constness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, constness: &$($lt)? $($mut)? Const) -> V::Result {
-            match constness {
-                Const::Yes(span) => visit_span(vis, span),
-                Const::No => {
-                    V::Result::output()
-                }
-            }
-        }
-
-        fn visit_defaultness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, defaultness: &$($lt)? $($mut)? Defaultness) -> V::Result {
-            match defaultness {
-                Defaultness::Default(span) => visit_span(vis, span),
-                Defaultness::Final => {
-                    V::Result::output()
-                }
-            }
-        }
-
-        fn visit_polarity<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            polarity: &$($lt)? $($mut)? ImplPolarity,
-        ) -> V::Result {
-            match polarity {
-                ImplPolarity::Positive => { V::Result::output() }
-                ImplPolarity::Negative(span) => visit_span(vis, span),
-            }
-        }
-
-        $(${ignore($lt)}
-            #[inline]
-        )?
-        fn visit_modifiers<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            m: &$($lt)? $($mut)? TraitBoundModifiers
-        ) -> V::Result {
-            let TraitBoundModifiers { constness, asyncness, polarity } = m;
-            match constness {
-                BoundConstness::Never => {}
-                BoundConstness::Always(span) | BoundConstness::Maybe(span) => try_visit!(visit_span(vis, span)),
-            }
-            match asyncness {
-                BoundAsyncness::Normal => {}
-                BoundAsyncness::Async(span) => try_visit!(visit_span(vis, span)),
-            }
-            match polarity {
-                BoundPolarity::Positive => {}
-                BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => try_visit!(visit_span(vis, span)),
-            }
+            $(${ignore($mut)} vis.visit_span(span))?;
             V::Result::output()
         }
 
-        $(${ignore($lt)}
-            #[inline]
-        )?
-        fn walk_capture_by<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            capture_by: &$($lt)? $($mut)? CaptureBy
-        ) -> V::Result {
-            match capture_by {
-                CaptureBy::Ref => { V::Result::output() }
-                CaptureBy::Value { move_kw } => {
-                    visit_span(vis, move_kw)
-                }
-                CaptureBy::Use { use_kw } => {
-                    visit_span(vis, use_kw)
-                }
+        $(impl_visitable!(|&$lt self: ThinVec<(UseTree, NodeId)>, vis: &mut V, _extra: ()| {
+            for (nested_tree, nested_id) in self {
+                try_visit!(vis.visit_nested_use_tree(nested_tree, *nested_id));
             }
-        }
-
-        fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) -> V::Result {
-            walk_list!(visitor, visit_param_bound, bounds, ctxt);
             V::Result::output()
-        }
+        });)?
+        $(impl_visitable_list!(<$mut> ThinVec<(UseTree, NodeId)>,);)?
 
-        pub fn walk_label<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Label { ident }: &$($lt)? $($mut)? Label) -> V::Result {
-            visitor.visit_ident(ident)
-        }
-
-        pub fn walk_fn_header<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, header: &$($lt)? $($mut)? FnHeader) -> V::Result {
-            let FnHeader { safety, coroutine_kind, constness, ext: _ } = header;
-            try_visit!(visit_constness(visitor, constness));
-            visit_opt!(visitor, visit_coroutine_kind, coroutine_kind);
-            visit_safety(visitor, safety)
-        }
-
-        pub fn walk_lifetime<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Lifetime { id, ident }: &$($lt)? $($mut)? Lifetime) -> V::Result {
-            try_visit!(visit_id(visitor, id));
-            visitor.visit_ident(ident)
-        }
-
-        fn walk_item_ctxt<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind>(
+        fn walk_item_inner<$($lt,)? K: WalkItemKind, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
             item: &$($mut)? $($lt)? Item<K>,
             ctxt: K::Ctxt,
         ) -> V::Result {
             let Item { attrs, id, kind, vis, span, tokens: _ } = item;
-            try_visit!(visit_id(visitor, id));
-            walk_list!(visitor, visit_attribute, attrs);
-            try_visit!(visitor.visit_vis(vis));
+            visit_visitable!($($mut)? visitor, id, attrs, vis);
             try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
-            visit_span(visitor, span)
+            visit_visitable!($($mut)? visitor, span);
+            V::Result::output()
         }
 
-        pub fn walk_item<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind<Ctxt = ()>>(
+        // Do not implement `Walkable`/`MutWalkable` for *Item to avoid confusion.
+        pub fn walk_item<$($lt,)? K: WalkItemKind<Ctxt = ()>, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
             item: &$($mut)? $($lt)? Item<K>,
         ) -> V::Result {
-            walk_item_ctxt(visitor, item, ())
+            walk_item_inner(visitor, item, ())
         }
 
-        pub fn walk_assoc_item<$($lt,)? V: $Visitor$(<$lt>)?>(
+        // Do not implement `Walkable`/`MutWalkable` for *Item to avoid confusion.
+        pub fn walk_assoc_item<$($lt,)? K: WalkItemKind<Ctxt = AssocCtxt>, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
-            item: &$($mut)? $($lt)? AssocItem,
+            item: &$($mut)? $($lt)? Item<K>,
             ctxt: AssocCtxt,
         ) -> V::Result {
-            walk_item_ctxt(visitor, item, ctxt)
+            walk_item_inner(visitor, item, ctxt)
         }
 
         impl WalkItemKind for ItemKind {
@@ -647,180 +807,52 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident),
-                    ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
-                    ItemKind::Static(box StaticItem {
-                        ident,
-                        ty,
-                        safety: _,
-                        mutability: _,
-                        expr,
-                        define_opaque,
-                    }) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_ty(ty));
-                        visit_opt!(vis, visit_expr, expr);
-                        walk_define_opaques(vis, define_opaque)
-                    }
-                    ItemKind::Const(item) => {
-                        walk_const_item(vis, item)
-                    }
                     ItemKind::Fn(func) => {
                         let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func);
-                        vis.visit_fn(kind, span, id)
-                    }
-                    ItemKind::Mod(safety, ident, mod_kind) => {
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_ident(ident));
-                        match mod_kind {
-                            ModKind::Loaded(
-                                items,
-                                _inline,
-                                ModSpans { inner_span, inject_use_span },
-                                _,
-                            ) => {
-                                try_visit!(visit_items(vis, items));
-                                try_visit!(visit_span(vis, inner_span));
-                                try_visit!(visit_span(vis, inject_use_span));
-                            }
-                            ModKind::Unloaded => {}
-                        }
-                        V::Result::output()
-                    }
-                    ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
-                    ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm),
-                    ItemKind::TyAlias(box TyAlias {
-                        defaultness,
-                        ident,
-                        generics,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                        bounds,
-                        ty,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    ItemKind::Enum(ident, generics, enum_definition) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        visit_variants(vis, &$($mut)? enum_definition.variants)
+                        try_visit!(vis.visit_fn(kind, span, id));
                     }
+                    ItemKind::ExternCrate(orig_name, ident) =>
+                        visit_visitable!($($mut)? vis, orig_name, ident),
+                    ItemKind::Use(use_tree) =>
+                        visit_visitable!($($mut)? vis, use_tree),
+                    ItemKind::Static(item) =>
+                        visit_visitable!($($mut)? vis, item),
+                    ItemKind::Const(item) =>
+                        visit_visitable!($($mut)? vis, item),
+                    ItemKind::Mod(safety, ident, mod_kind) =>
+                        visit_visitable!($($mut)? vis, safety, ident, mod_kind),
+                    ItemKind::ForeignMod(nm) =>
+                        visit_visitable!($($mut)? vis, nm),
+                    ItemKind::GlobalAsm(asm) =>
+                        visit_visitable!($($mut)? vis, asm),
+                    ItemKind::TyAlias(ty_alias) =>
+                        visit_visitable!($($mut)? vis, ty_alias),
+                    ItemKind::Enum(ident, generics, enum_definition) =>
+                        visit_visitable!($($mut)? vis, ident, generics, enum_definition),
                     ItemKind::Struct(ident, generics, variant_data)
-                    | ItemKind::Union(ident, generics, variant_data) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        vis.visit_variant_data(variant_data)
-                    }
-                    ItemKind::Impl(box Impl {
-                        defaultness,
-                        safety,
-                        generics,
-                        constness,
-                        polarity,
-                        of_trait,
-                        self_ty,
-                        items,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_constness(vis, constness));
-                        try_visit!(visit_polarity(vis, polarity));
-                        visit_opt!(vis, visit_trait_ref, of_trait);
-                        try_visit!(vis.visit_ty(self_ty));
-                        visit_assoc_items(vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() })
-                    }
-                    ItemKind::Trait(box Trait { constness, safety, is_auto: _, ident, generics, bounds, items }) => {
-                        try_visit!(visit_constness(vis, constness));
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_assoc_items(vis, items, AssocCtxt::Trait)
-                    }
+                    | ItemKind::Union(ident, generics, variant_data) =>
+                        visit_visitable!($($mut)? vis, ident, generics, variant_data),
+                    ItemKind::Impl(impl_) =>
+                        visit_visitable!($($mut)? vis, impl_),
+                    ItemKind::Trait(trait_) =>
+                        visit_visitable!($($mut)? vis, trait_),
                     ItemKind::TraitAlias(ident, generics, bounds) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        visit_bounds(vis, bounds, BoundKind::Bound)
-                    }
-                    ItemKind::MacCall(m) => vis.visit_mac_call(m),
-                    ItemKind::MacroDef(ident, def) => {
-                        try_visit!(vis.visit_ident(ident));
-                        vis.visit_macro_def(def)
-                    }
-                    ItemKind::Delegation(box Delegation {
-                        id,
-                        qself,
-                        path,
-                        ident,
-                        rename,
-                        body,
-                        from_glob: _,
-                    }) => {
-                        try_visit!(visit_id(vis, id));
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(path));
-                        try_visit!(vis.visit_ident(ident));
-                        visit_opt!(vis, visit_ident, rename);
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
-                    }
-                    ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(prefix));
-                        if let Some(suffixes) = suffixes {
-                            for (ident, rename) in suffixes {
-                                try_visit!(vis.visit_ident(ident));
-                                visit_opt!(vis, visit_ident, rename);
-                            }
-                        }
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
+                        visit_visitable!($($mut)? vis, ident, generics);
+                        visit_visitable_with!($($mut)? vis, bounds, BoundKind::Bound)
                     }
+                    ItemKind::MacCall(m) =>
+                        visit_visitable!($($mut)? vis, m),
+                    ItemKind::MacroDef(ident, def) =>
+                        visit_visitable!($($mut)? vis, ident, def),
+                    ItemKind::Delegation(delegation) =>
+                        visit_visitable!($($mut)? vis, delegation),
+                    ItemKind::DelegationMac(dm) =>
+                        visit_visitable!($($mut)? vis, dm),
                 }
+                V::Result::output()
             }
         }
 
-        fn walk_const_item<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            item: &$($lt)? $($mut)? ConstItem,
-        ) -> V::Result {
-            let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item;
-            try_visit!(visit_defaultness(vis, defaultness));
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_generics(generics));
-            try_visit!(vis.visit_ty(ty));
-            visit_opt!(vis, visit_expr, expr);
-            walk_define_opaques(vis, define_opaque)
-        }
-
-        fn walk_foreign_mod<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, foreign_mod: &$($lt)? $($mut)? ForeignMod) -> V::Result {
-            let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod;
-            try_visit!(visit_safety(vis, safety));
-            visit_foreign_items(vis, items)
-        }
-
-        fn walk_define_opaques<$($lt,)? V: $Visitor$(<$lt>)?>(
-            visitor: &mut V,
-            define_opaque: &$($lt)? $($mut)? Option<ThinVec<(NodeId, Path)>>,
-        ) -> V::Result {
-            if let Some(define_opaque) = define_opaque {
-                for (id, path) in define_opaque {
-                    try_visit!(visit_id(visitor, id));
-                    try_visit!(visitor.visit_path(path));
-                }
-            }
-            V::Result::output()
-        }
-
         impl WalkItemKind for AssocItemKind {
             type Ctxt = AssocCtxt;
             fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
@@ -832,64 +864,22 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    AssocItemKind::Const(item) => {
-                        walk_const_item(vis, item)
-                    }
+                    AssocItemKind::Const(item) =>
+                        visit_visitable!($($mut)? vis, item),
                     AssocItemKind::Fn(func) => {
-                        vis.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func), span, id)
-                    }
-                    AssocItemKind::Type(box TyAlias {
-                        generics,
-                        ident,
-                        bounds,
-                        ty,
-                        defaultness,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    AssocItemKind::MacCall(mac) => {
-                        vis.visit_mac_call(mac)
-                    }
-                    AssocItemKind::Delegation(box Delegation {
-                        id,
-                        qself,
-                        path,
-                        ident,
-                        rename,
-                        body,
-                        from_glob: _,
-                    }) => {
-                        try_visit!(visit_id(vis, id));
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(path));
-                        try_visit!(vis.visit_ident(ident));
-                        visit_opt!(vis, visit_ident, rename);
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
-                    }
-                    AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(prefix));
-                        if let Some(suffixes) = suffixes {
-                            for (ident, rename) in suffixes {
-                                try_visit!(vis.visit_ident(ident));
-                                visit_opt!(vis, visit_ident, rename);
-                            }
-                        }
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
+                        let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func);
+                        try_visit!(vis.visit_fn(kind, span, id))
                     }
+                    AssocItemKind::Type(alias) =>
+                        visit_visitable!($($mut)? vis, alias),
+                    AssocItemKind::MacCall(mac) =>
+                        visit_visitable!($($mut)? vis, mac),
+                    AssocItemKind::Delegation(delegation) =>
+                        visit_visitable!($($mut)? vis, delegation),
+                    AssocItemKind::DelegationMac(dm) =>
+                        visit_visitable!($($mut)? vis, dm),
                 }
+                V::Result::output()
             }
         }
 
@@ -904,545 +894,18 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    ForeignItemKind::Static(box StaticItem {
-                        ident,
-                        ty,
-                        mutability: _,
-                        expr,
-                        safety: _,
-                        define_opaque,
-                    }) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_ty(ty));
-                        visit_opt!(vis, visit_expr, expr);
-                        walk_define_opaques(vis, define_opaque)
-                    }
+                    ForeignItemKind::Static(item) =>
+                        visit_visitable!($($mut)? vis, item),
                     ForeignItemKind::Fn(func) => {
-                        vis.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func), span, id)
-                    }
-                    ForeignItemKind::TyAlias(box TyAlias {
-                        defaultness,
-                        ident,
-                        generics,
-                        bounds,
-                        ty,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    ForeignItemKind::MacCall(mac) => {
-                        vis.visit_mac_call(mac)
-                    }
-                }
-            }
-        }
-
-        fn walk_coroutine_kind<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            coroutine_kind: &$($lt)? $($mut)? CoroutineKind,
-        ) -> V::Result {
-            let (CoroutineKind::Async { span, closure_id, return_impl_trait_id }
-                | CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
-                | CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id })
-                = coroutine_kind;
-            try_visit!(visit_id(vis, closure_id));
-            try_visit!(visit_id(vis, return_impl_trait_id));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            pattern: &$($lt)? $($mut)? Pat
-        ) -> V::Result {
-            let Pat { id, kind, span, tokens: _ } = pattern;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                PatKind::Err(_guar) => {}
-                PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
-                PatKind::Ident(_bmode, ident, optional_subpattern) => {
-                    try_visit!(vis.visit_ident(ident));
-                    visit_opt!(vis, visit_pat, optional_subpattern);
-                }
-                PatKind::Expr(expression) => try_visit!(vis.visit_expr(expression)),
-                PatKind::TupleStruct(opt_qself, path, elems) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path));
-                    walk_list!(vis, visit_pat, elems);
-                }
-                PatKind::Path(opt_qself, path) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path))
-                }
-                PatKind::Struct(opt_qself, path, fields, _rest) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(visit_pat_fields(vis, fields));
-                }
-                PatKind::Box(subpattern) | PatKind::Deref(subpattern) | PatKind::Paren(subpattern) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                }
-                PatKind::Ref(subpattern, _ /*mutbl*/) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                }
-                PatKind::Range(lower_bound, upper_bound, _end) => {
-                    visit_opt!(vis, visit_expr, lower_bound);
-                    visit_opt!(vis, visit_expr, upper_bound);
-                    try_visit!(visit_span(vis, span));
-                }
-                PatKind::Guard(subpattern, guard_condition) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                    try_visit!(vis.visit_expr(guard_condition));
-                }
-                PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
-                    walk_list!(vis, visit_pat, elems);
-                }
-                PatKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_anon_const<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            constant: &$($lt)? $($mut)? AnonConst,
-        ) -> V::Result {
-            let AnonConst { id, value } = constant;
-            try_visit!(visit_id(vis, id));
-            vis.visit_expr(value)
-        }
-
-        pub fn walk_path_segment<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            segment: &$($lt)? $($mut)? PathSegment,
-        ) -> V::Result {
-            let PathSegment { ident, id, args } = segment;
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_ident(ident));
-            visit_opt!(vis, visit_generic_args, args);
-            V::Result::output()
-        }
-
-        pub fn walk_block<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            block: &$($lt)? $($mut)? Block
-        ) -> V::Result {
-            let Block { stmts, id, rules: _, span, tokens: _ } = block;
-            try_visit!(visit_id(vis, id));
-            try_visit!(visit_stmts(vis, stmts));
-            visit_span(vis, span)
-        }
-
-
-        pub fn walk_ty<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V, ty: &$($lt)? $($mut)? Ty
-        ) -> V::Result {
-            let Ty { id, kind, span, tokens: _ } = ty;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                TyKind::Err(_guar) => {}
-                TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => {}
-                TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(vis.visit_ty(ty)),
-                TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(vis.visit_ty(ty)),
-                TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
-                | TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
-                    // FIXME(fee1-dead) asymmetry
-                    visit_opt!(vis, visit_lifetime, opt_lifetime$(${ignore($lt)}, LifetimeCtxt::Ref)?);
-                    try_visit!(vis.visit_ty(ty));
-                }
-                TyKind::Tup(tuple_element_types) => {
-                    walk_list!(vis, visit_ty, tuple_element_types);
-                }
-                TyKind::FnPtr(function_declaration) => {
-                    let FnPtrTy { safety, ext: _, generic_params, decl, decl_span } =
-                        &$($mut)? **function_declaration;
-                    try_visit!(visit_safety(vis, safety));
-                    try_visit!(visit_generic_params(vis, generic_params));
-                    try_visit!(vis.visit_fn_decl(decl));
-                    try_visit!(visit_span(vis, decl_span));
-                }
-                TyKind::UnsafeBinder(binder) => {
-                    try_visit!(visit_generic_params(vis, &$($mut)? binder.generic_params));
-                    try_visit!(vis.visit_ty(&$($mut)? binder.inner_ty));
-                }
-                TyKind::Path(maybe_qself, path) => {
-                    try_visit!(vis.visit_qself(maybe_qself));
-                    try_visit!(vis.visit_path(path));
-                }
-                TyKind::Pat(ty, pat) => {
-                    try_visit!(vis.visit_ty(ty));
-                    try_visit!(vis.visit_ty_pat(pat));
-                }
-                TyKind::Array(ty, length) => {
-                    try_visit!(vis.visit_ty(ty));
-                    try_visit!(vis.visit_anon_const(length));
-                }
-                TyKind::TraitObject(bounds, _syntax) => {
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::TraitObject);
-                }
-                TyKind::ImplTrait(id, bounds) => {
-                    try_visit!(visit_id(vis, id));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Impl);
-                }
-                TyKind::Typeof(expression) => try_visit!(vis.visit_anon_const(expression)),
-
-                TyKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_crate<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            krate: &$($lt)? $($mut)? Crate,
-        ) -> V::Result {
-            let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(visit_items(vis, items));
-            let ModSpans { inner_span, inject_use_span } = spans;
-            try_visit!(visit_span(vis, inner_span));
-            visit_span(vis, inject_use_span)
-        }
-
-        pub fn walk_local<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            local: &$($lt)? $($mut)? Local,
-        ) -> V::Result {
-            let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local;
-            if let Some(sp) = super_ {
-                try_visit!(visit_span(vis, sp));
-            }
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            visit_opt!(vis, visit_ty, ty);
-            match kind {
-                LocalKind::Decl => {}
-                LocalKind::Init(init) => {
-                    try_visit!(vis.visit_expr(init))
-                }
-                LocalKind::InitElse(init, els) => {
-                    try_visit!(vis.visit_expr(init));
-                    try_visit!(vis.visit_block(els));
-                }
-            }
-            if let Some(sp) = colon_sp {
-                try_visit!(visit_span(vis, sp));
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_poly_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            p: &$($lt)? $($mut)? PolyTraitRef,
-        ) -> V::Result {
-            let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span, parens: _ } = p;
-            try_visit!(visit_modifiers(vis, modifiers));
-            try_visit!(visit_generic_params(vis, bound_generic_params));
-            try_visit!(vis.visit_trait_ref(trait_ref));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            TraitRef { path, ref_id }: &$($lt)? $($mut)? TraitRef,
-        ) -> V::Result {
-            try_visit!(vis.visit_path(path));
-            visit_id(vis, ref_id)
-        }
-
-        pub fn walk_variant<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            variant: &$($lt)? $($mut)? Variant,
-        ) -> V::Result {
-            let Variant { attrs, id, span, vis: visibility, ident, data, disr_expr, is_placeholder: _ } = variant;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_vis(visibility));
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_variant_data(data));
-            visit_opt!(vis, visit_anon_const, disr_expr);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_expr_field<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            f: &$($lt)? $($mut)? ExprField,
-        ) -> V::Result {
-            let ExprField { attrs, id, span, ident, expr, is_shorthand: _, is_placeholder: _ } = f;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_expr(expr));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_pat_field<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            fp: &$($lt)? $($mut)? PatField,
-        ) -> V::Result {
-            let PatField { ident, pat, is_shorthand: _, attrs, id, span, is_placeholder: _ } = fp;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_pat(pat));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_ty_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            tp: &$($lt)? $($mut)? TyPat,
-        ) -> V::Result {
-            let TyPat { id, kind, span, tokens: _ } = tp;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                TyPatKind::Range(start, end, Spanned { span, node: _include_end }) => {
-                    visit_opt!(vis, visit_anon_const, start);
-                    visit_opt!(vis, visit_anon_const, end);
-                    try_visit!(visit_span(vis, span));
-                }
-                TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants),
-                TyPatKind::Err(_) => {}
-            }
-            visit_span(vis, span)
-        }
-
-        fn walk_qself<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            qself: &$($lt)? $($mut)? Option<P<QSelf>>,
-        ) -> V::Result {
-            if let Some(qself) = qself {
-                let QSelf { ty, path_span, position: _ } = &$($mut)? **qself;
-                try_visit!(vis.visit_ty(ty));
-                try_visit!(visit_span(vis, path_span));
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_path<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            path: &$($lt)? $($mut)? Path,
-        ) -> V::Result {
-            let Path { span, segments, tokens: _ } = path;
-            walk_list!(vis, visit_path_segment, segments);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_use_tree<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            use_tree: &$($lt)? $($mut)? UseTree,
-        ) -> V::Result {
-            let UseTree { prefix, kind, span } = use_tree;
-            try_visit!(vis.visit_path(prefix));
-            match kind {
-                UseTreeKind::Simple(rename) => {
-                    // The extra IDs are handled during AST lowering.
-                    visit_opt!(vis, visit_ident, rename);
-                }
-                UseTreeKind::Glob => {}
-                UseTreeKind::Nested { items, span } => {
-                    for (nested_tree, nested_id) in items {
-                        try_visit!(visit_nested_use_tree(vis, nested_tree, nested_id));
+                        let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func);
+                        try_visit!(vis.visit_fn(kind, span, id))
                     }
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_generic_args<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            generic_args: &$($lt)? $($mut)? GenericArgs
-        ) -> V::Result {
-            match generic_args {
-                GenericArgs::AngleBracketed(AngleBracketedArgs { span, args }) => {
-                    for arg in args {
-                        match arg {
-                            AngleBracketedArg::Arg(a) => try_visit!(vis.visit_generic_arg(a)),
-                            AngleBracketedArg::Constraint(c) => {
-                                try_visit!(vis.visit_assoc_item_constraint(c))
-                            }
-                        }
-                    }
-                    visit_span(vis, span)
-                }
-                GenericArgs::Parenthesized(data) => {
-                    let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
-                    walk_list!(vis, visit_ty, inputs);
-                    try_visit!(vis.visit_fn_ret_ty(output));
-                    try_visit!(visit_span(vis, span));
-                    visit_span(vis, inputs_span)
-                }
-                GenericArgs::ParenthesizedElided(span) => visit_span(vis, span)
-            }
-        }
-
-        pub fn walk_generic_arg<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            generic_arg: &$($lt)? $($mut)? GenericArg,
-        ) -> V::Result {
-            match generic_arg {
-                GenericArg::Lifetime(lt) => vis.visit_lifetime(lt, $(${ignore($lt)} LifetimeCtxt::GenericArg)? ),
-                GenericArg::Type(ty) => vis.visit_ty(ty),
-                GenericArg::Const(ct) => vis.visit_anon_const(ct),
-            }
-        }
-
-        pub fn walk_assoc_item_constraint<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            constraint: &$($lt)? $($mut)? AssocItemConstraint,
-        ) -> V::Result {
-            let AssocItemConstraint { id, ident, gen_args, kind, span } = constraint;
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_ident(ident));
-            visit_opt!(vis, visit_generic_args, gen_args);
-            match kind {
-                AssocItemConstraintKind::Equality { term } => match term {
-                    Term::Ty(ty) => try_visit!(vis.visit_ty(ty)),
-                    Term::Const(c) => try_visit!(vis.visit_anon_const(c)),
-                },
-                AssocItemConstraintKind::Bound { bounds } => {
-                    try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                }
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_param_bound<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, bound: &$($lt)? $($mut)? GenericBound) -> V::Result {
-            match bound {
-                GenericBound::Trait(trait_ref) => vis.visit_poly_trait_ref(trait_ref),
-                GenericBound::Outlives(lifetime) => vis.visit_lifetime(lifetime, $(${ignore($lt)} LifetimeCtxt::Bound)?),
-                GenericBound::Use(args, span) => {
-                    walk_list!(vis, visit_precise_capturing_arg, args);
-                    visit_span(vis, span)
-                }
-            }
-        }
-
-        pub fn walk_precise_capturing_arg<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            arg: &$($lt)? $($mut)? PreciseCapturingArg,
-        ) -> V::Result {
-            match arg {
-                PreciseCapturingArg::Lifetime(lt) => vis.visit_lifetime(lt, $(${ignore($lt)} LifetimeCtxt::GenericArg)?),
-                PreciseCapturingArg::Arg(path, id) => {
-                    try_visit!(visit_id(vis, id));
-                    vis.visit_path(path)
-                }
-            }
-        }
-
-        pub fn walk_generic_param<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            param: &$($lt)? $($mut)? GenericParam,
-        ) -> V::Result {
-            let GenericParam { id, ident, attrs, bounds, is_placeholder: _, kind, colon_span } =
-                param;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-            match kind {
-                GenericParamKind::Lifetime => (),
-                GenericParamKind::Type { default } => visit_opt!(vis, visit_ty, default),
-                GenericParamKind::Const { ty, default, span } => {
-                    try_visit!(vis.visit_ty(ty));
-                    visit_opt!(vis, visit_anon_const, default);
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            if let Some(sp) = colon_span {
-                try_visit!(visit_span(vis, sp))
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_generics<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, generics: &$($lt)? $($mut)? Generics) -> V::Result {
-            let Generics { params, where_clause, span } = generics;
-            let WhereClause { has_where_token: _, predicates, span: where_clause_span } = where_clause;
-            try_visit!(visit_generic_params(vis, params));
-            try_visit!(visit_where_predicates(vis, predicates));
-            try_visit!(visit_span(vis, span));
-            visit_span(vis, where_clause_span)
-        }
-
-        pub fn walk_contract<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, c: &$($lt)? $($mut)? FnContract) -> V::Result {
-            let FnContract { requires, ensures } = c;
-            visit_opt!(vis, visit_expr, requires);
-            visit_opt!(vis, visit_expr, ensures);
-            V::Result::output()
-        }
-
-        pub fn walk_where_predicate<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            predicate: &$($lt)? $($mut)? WherePredicate,
-        ) -> V::Result {
-            let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = predicate;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(visit_span(vis, span));
-            vis.visit_where_predicate_kind(kind)
-        }
-
-        pub fn walk_closure_binder<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            binder: &$($lt)? $($mut)? ClosureBinder,
-        ) -> V::Result {
-            match binder {
-                ClosureBinder::NotPresent => {}
-                ClosureBinder::For { generic_params, span } => {
-                    try_visit!(visit_generic_params(vis, generic_params));
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_where_predicate_kind<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            kind: &$($lt)? $($mut)? WherePredicateKind,
-        ) -> V::Result {
-            match kind {
-                WherePredicateKind::BoundPredicate(WhereBoundPredicate {
-                    bounded_ty,
-                    bounds,
-                    bound_generic_params,
-                }) => {
-                    try_visit!(visit_generic_params(vis, bound_generic_params));
-                    try_visit!(vis.visit_ty(bounded_ty));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-                }
-                WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => {
-                    try_visit!(vis.visit_lifetime(lifetime, $(${ignore($lt)} LifetimeCtxt::Bound )?));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-                }
-                WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
-                    try_visit!(vis.visit_ty(lhs_ty));
-                    try_visit!(vis.visit_ty(rhs_ty));
+                    ForeignItemKind::TyAlias(alias) =>
+                        visit_visitable!($($mut)? vis, alias),
+                    ForeignItemKind::MacCall(mac) =>
+                        visit_visitable!($($mut)? vis, mac),
                 }
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_fn_decl<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            FnDecl { inputs, output }: &$($lt)? $($mut)? FnDecl,
-        ) -> V::Result {
-            try_visit!(visit_params(vis, inputs));
-            vis.visit_fn_ret_ty(output)
-        }
-
-        pub fn walk_fn_ret_ty<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, ret_ty: &$($lt)? $($mut)? FnRetTy) -> V::Result {
-            match ret_ty {
-                FnRetTy::Default(span) => visit_span(vis, span),
-                FnRetTy::Ty(output_ty) => vis.visit_ty(output_ty),
+                V::Result::output()
             }
         }
 
@@ -1450,455 +913,200 @@ macro_rules! common_visitor_and_walkers {
             match kind {
                 FnKind::Fn(
                     _ctxt,
+                    // Visibility is visited as a part of the item.
                     _vis,
-                    Fn {
-                        defaultness,
-                        ident,
-                        sig: FnSig { header, decl, span },
-                        generics,
-                        contract,
-                        body,
-                        define_opaque,
-                    },
+                    Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
                 ) => {
-                    // Visibility is visited as a part of the item.
-                    try_visit!(visit_defaultness(vis, defaultness));
-                    try_visit!(vis.visit_ident(ident));
-                    try_visit!(vis.visit_fn_header(header));
-                    try_visit!(vis.visit_generics(generics));
-                    try_visit!(vis.visit_fn_decl(decl));
-                    visit_opt!(vis, visit_contract, contract);
-                    visit_opt!(vis, visit_block, body);
-                    try_visit!(visit_span(vis, span));
-                    walk_define_opaques(vis, define_opaque)
-                }
-                FnKind::Closure(binder, coroutine_kind, decl, body) => {
-                    try_visit!(vis.visit_closure_binder(binder));
-                    visit_opt!(vis, visit_coroutine_kind, coroutine_kind);
-                    try_visit!(vis.visit_fn_decl(decl));
-                    vis.visit_expr(body)
-                }
-            }
-        }
-
-        pub fn walk_variant_data<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, data: &$($lt)? $($mut)? VariantData) -> V::Result {
-            match data {
-                VariantData::Struct { fields, recovered: _ } => {
-                    visit_field_defs(vis, fields)
-                }
-                VariantData::Tuple(fields, id) => {
-                    try_visit!(visit_id(vis, id));
-                    visit_field_defs(vis, fields)
-                }
-                VariantData::Unit(id) => visit_id(vis, id),
-            }
-        }
-
-        pub fn walk_field_def<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, field: &$($lt)? $($mut)? FieldDef) -> V::Result {
-            let FieldDef { attrs, id, span, vis: visibility, ident, ty, is_placeholder: _, safety: _, default } =
-                field;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_vis(visibility));
-            visit_opt!(vis, visit_ident, ident);
-            try_visit!(vis.visit_ty(ty));
-            visit_opt!(vis, visit_anon_const, default);
-            visit_span(vis, span)
-        }
-
-        fn visit_delim_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, args: &$($lt)? $($mut)? DelimArgs) -> V::Result {
-            let DelimArgs { dspan, delim: _, tokens: _ } = args;
-            let DelimSpan { open, close } = dspan;
-            try_visit!(visit_span(vis, open));
-            visit_span(vis, close)
-        }
-
-        pub fn walk_mac<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, mac: &$($lt)? $($mut)? MacCall) -> V::Result {
-            let MacCall { path, args } = mac;
-            try_visit!(vis.visit_path(path));
-            visit_delim_args(vis, args)
-        }
-
-        fn walk_macro_def<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, macro_def: &$($lt)? $($mut)? MacroDef) -> V::Result {
-            let MacroDef { body, macro_rules: _ } = macro_def;
-            visit_delim_args(vis, body)
-        }
-
-        pub fn walk_inline_asm<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, asm: &$($lt)? $($mut)? InlineAsm) -> V::Result {
-            let InlineAsm {
-                asm_macro: _,
-                template,
-                template_strs,
-                operands,
-                clobber_abis,
-                options: _,
-                line_spans,
-            } = asm;
-            for piece in template {
-                match piece {
-                    InlineAsmTemplatePiece::String(_str) => {}
-                    InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span } => {
-                        try_visit!(visit_span(vis, span));
-                    }
+                    let FnSig { header, decl, span } = sig;
+                    visit_visitable!($($mut)? vis,
+                        defaultness, ident, header, generics, decl,
+                        contract, body, span, define_opaque
+                    )
                 }
-            }
-            for (_s1, _s2, span) in template_strs {
-                try_visit!(visit_span(vis, span));
-            }
-            for (op, span) in operands {
-                match op {
-                    InlineAsmOperand::In { expr, reg: _ }
-                    | InlineAsmOperand::Out { expr: Some(expr), reg: _, late: _ }
-                    | InlineAsmOperand::InOut { expr, reg: _, late: _ } => {
-                        try_visit!(vis.visit_expr(expr))
-                    }
-                    InlineAsmOperand::Out { expr: None, reg: _, late: _ } => {}
-                    InlineAsmOperand::SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
-                        try_visit!(vis.visit_expr(in_expr));
-                        visit_opt!(vis, visit_expr, out_expr);
-                    }
-                    InlineAsmOperand::Const { anon_const } => {
-                        try_visit!(vis.visit_anon_const(anon_const))
-                    }
-                    InlineAsmOperand::Sym { sym } => try_visit!(vis.visit_inline_asm_sym(sym)),
-                    InlineAsmOperand::Label { block } => try_visit!(vis.visit_block(block)),
-                }
-                try_visit!(visit_span(vis, span));
-            }
-            for (_s1, span) in clobber_abis {
-                try_visit!(visit_span(vis, span))
-            }
-            for span in line_spans {
-                try_visit!(visit_span(vis, span))
+                FnKind::Closure(binder, coroutine_kind, decl, body) =>
+                    visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
             }
             V::Result::output()
         }
 
-        pub fn walk_inline_asm_sym<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            InlineAsmSym { id, qself, path }: &$($lt)? $($mut)? InlineAsmSym,
-        ) -> V::Result {
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_qself(qself));
-            vis.visit_path(path)
-        }
-
-        pub fn walk_format_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, fmt: &$($lt)? $($mut)? FormatArgs) -> V::Result {
-            let FormatArgs { span, template, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
-
-            let args = $(${ignore($mut)} arguments.all_args_mut())? $(${ignore($lt)} arguments.all_args())? ;
-            for FormatArgument { kind, expr } in args {
-                match kind {
-                    FormatArgumentKind::Named(ident) | FormatArgumentKind::Captured(ident) => {
-                        try_visit!(vis.visit_ident(ident))
-                    }
-                    FormatArgumentKind::Normal => {}
-                }
-                try_visit!(vis.visit_expr(expr));
-            }
-            for piece in template {
-                match piece {
-                    FormatArgsPiece::Literal(_symbol) => {}
-                    FormatArgsPiece::Placeholder(placeholder) => try_visit!(walk_format_placeholder(vis, placeholder)),
-                }
-            }
-            visit_span(vis, span)
-        }
+        impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| {
+            let Impl { defaultness, safety, generics, constness, polarity, of_trait, self_ty, items } = self;
+            visit_visitable!($($mut)? vis, defaultness, safety, generics, constness, polarity, of_trait, self_ty);
+            visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() });
+            V::Result::output()
+        });
 
-        fn walk_format_placeholder<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            placeholder: &$($lt)? $($mut)? FormatPlaceholder,
-        ) -> V::Result {
-            let FormatPlaceholder { argument, span, format_options, format_trait: _ } = placeholder;
-            if let Some(span) = span {
-                try_visit!(visit_span(vis, span));
-            }
-            let FormatArgPosition { span, index: _, kind: _ } = argument;
-            if let Some(span) = span {
-                try_visit!(visit_span(vis, span));
-            }
-            let FormatOptions {
-                width,
-                precision,
-                alignment: _,
-                fill: _,
-                sign: _,
-                alternate: _,
-                zero_pad: _,
-                debug_hex: _,
-            } = format_options;
-            match width {
-                None => {}
-                Some(FormatCount::Literal(_)) => {}
-                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
-                    if let Some(span) = span {
-                        try_visit!(visit_span(vis, span));
-                    }
-                }
-            }
-            match precision {
-                None => {}
-                Some(FormatCount::Literal(_)) => {}
-                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
-                    if let Some(span) = span {
-                        try_visit!(visit_span(vis, span));
-                    }
-                }
-            }
+        // Special case to call `visit_method_receiver_expr`.
+        impl_walkable!(|&$($mut)? $($lt)? self: MethodCall, vis: &mut V| {
+            let MethodCall { seg, receiver, args, span } = self;
+            try_visit!(vis.visit_method_receiver_expr(receiver));
+            visit_visitable!($($mut)? vis, seg, args, span);
             V::Result::output()
-        }
+        });
 
-        pub fn walk_expr<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, expression: &$($lt)? $($mut)? Expr) -> V::Result {
-            let Expr { id, kind, span, attrs, tokens: _ } = expression;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
+        impl_walkable!(|&$($mut)? $($lt)? self: Expr, vis: &mut V| {
+            let Expr { id, kind, span, attrs, tokens: _ } = self;
+            visit_visitable!($($mut)? vis, id, attrs);
             match kind {
-                ExprKind::Array(exprs) => {
-                    try_visit!(visit_exprs(vis, exprs));
-                }
-                ExprKind::ConstBlock(anon_const) => try_visit!(vis.visit_anon_const(anon_const)),
-                ExprKind::Repeat(element, count) => {
-                    try_visit!(vis.visit_expr(element));
-                    try_visit!(vis.visit_anon_const(count));
-                }
-                ExprKind::Struct(se) => {
-                    let StructExpr { qself, path, fields, rest } = &$($mut)?**se;
-                    try_visit!(vis.visit_qself(qself));
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(visit_expr_fields(vis, fields));
-                    match rest {
-                        StructRest::Base(expr) => try_visit!(vis.visit_expr(expr)),
-                        StructRest::Rest(span) => try_visit!(visit_span(vis, span)),
-                        StructRest::None => {}
-                    }
-                }
-                ExprKind::Tup(exprs) => {
-                    try_visit!(visit_exprs(vis, exprs));
-                }
-                ExprKind::Call(callee_expression, arguments) => {
-                    try_visit!(vis.visit_expr(callee_expression));
-                    try_visit!(visit_exprs(vis, arguments));
-                }
-                ExprKind::MethodCall(box MethodCall { seg, receiver, args, span }) => {
-                    try_visit!(vis.visit_method_receiver_expr(receiver));
-                    try_visit!(vis.visit_path_segment(seg));
-                    try_visit!(visit_exprs(vis, args));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Binary(Spanned { span, node: _ }, left_expression, right_expression) => {
-                    try_visit!(vis.visit_expr(left_expression));
-                    try_visit!(vis.visit_expr(right_expression));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::AddrOf(_kind, _mutbl, subexpression) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                }
-                ExprKind::Unary(_op, subexpression) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                }
-                ExprKind::Cast(subexpression, typ) | ExprKind::Type(subexpression, typ) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_ty(typ));
-                }
-                ExprKind::Let(pat, expr, span, _recovered) => {
-                    try_visit!(vis.visit_pat(pat));
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::If(head_expression, if_block, optional_else) => {
-                    try_visit!(vis.visit_expr(head_expression));
-                    try_visit!(vis.visit_block(if_block));
-                    visit_opt!(vis, visit_expr, optional_else);
-                }
-                ExprKind::While(subexpression, block, opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_block(block));
-                }
-                ExprKind::ForLoop { pat, iter, body, label, kind: _ } => {
-                    visit_opt!(vis, visit_label, label);
-                    try_visit!(vis.visit_pat(pat));
-                    try_visit!(vis.visit_expr(iter));
-                    try_visit!(vis.visit_block(body));
-                }
-                ExprKind::Loop(block, opt_label, span) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_block(block));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::Match(subexpression, arms, _kind) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(visit_arms(vis, arms));
-                }
+                ExprKind::Array(exprs) =>
+                    visit_visitable!($($mut)? vis, exprs),
+                ExprKind::ConstBlock(anon_const) =>
+                    visit_visitable!($($mut)? vis, anon_const),
+                ExprKind::Repeat(element, count) =>
+                    visit_visitable!($($mut)? vis, element, count),
+                ExprKind::Struct(se) =>
+                    visit_visitable!($($mut)? vis, se),
+                ExprKind::Tup(exprs) =>
+                    visit_visitable!($($mut)? vis, exprs),
+                ExprKind::Call(callee_expression, arguments) =>
+                    visit_visitable!($($mut)? vis, callee_expression, arguments),
+                ExprKind::MethodCall(mc) =>
+                    visit_visitable!($($mut)? vis, mc),
+                ExprKind::Binary(op, lhs, rhs) =>
+                    visit_visitable!($($mut)? vis, op, lhs, rhs),
+                ExprKind::AddrOf(kind, mutbl, subexpression) =>
+                    visit_visitable!($($mut)? vis, kind, mutbl, subexpression),
+                ExprKind::Unary(op, subexpression) =>
+                    visit_visitable!($($mut)? vis, op, subexpression),
+                ExprKind::Cast(subexpression, typ) | ExprKind::Type(subexpression, typ) =>
+                    visit_visitable!($($mut)? vis, subexpression, typ),
+                ExprKind::Let(pat, expr, span, _recovered) =>
+                    visit_visitable!($($mut)? vis, pat, expr, span),
+                ExprKind::If(head_expression, if_block, optional_else) =>
+                    visit_visitable!($($mut)? vis, head_expression, if_block, optional_else),
+                ExprKind::While(subexpression, block, opt_label) =>
+                    visit_visitable!($($mut)? vis, subexpression, block, opt_label),
+                ExprKind::ForLoop { pat, iter, body, label, kind } =>
+                    visit_visitable!($($mut)? vis, pat, iter, body, label, kind),
+                ExprKind::Loop(block, opt_label, span) =>
+                    visit_visitable!($($mut)? vis, block, opt_label, span),
+                ExprKind::Match(subexpression, arms, kind) =>
+                    visit_visitable!($($mut)? vis, subexpression, arms, kind),
                 ExprKind::Closure(box Closure {
                     binder,
                     capture_clause,
                     coroutine_kind,
                     constness,
-                    movability: _,
+                    movability,
                     fn_decl,
                     body,
                     fn_decl_span,
                     fn_arg_span,
                 }) => {
-                    try_visit!(visit_constness(vis, constness));
-                    try_visit!(vis.visit_capture_by(capture_clause));
-                    try_visit!(vis.visit_fn(
-                        FnKind::Closure(binder, coroutine_kind, fn_decl, body),
-                        *span,
-                        *id
-                    ));
-                    try_visit!(visit_span(vis, fn_decl_span));
-                    try_visit!(visit_span(vis, fn_arg_span));
-                }
-                ExprKind::Block(block, opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_block(block));
-                }
-                ExprKind::Gen(capture_clause, body, _kind, decl_span) => {
-                    try_visit!(vis.visit_capture_by(capture_clause));
-                    try_visit!(vis.visit_block(body));
-                    try_visit!(visit_span(vis, decl_span));
-                }
-                ExprKind::Await(expr, span) => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Use(expr, span) => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Assign(lhs, rhs, span) => {
-                    try_visit!(vis.visit_expr(lhs));
-                    try_visit!(vis.visit_expr(rhs));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::AssignOp(Spanned { span, node: _ }, left_expression, right_expression) => {
-                    try_visit!(vis.visit_expr(left_expression));
-                    try_visit!(vis.visit_expr(right_expression));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Field(subexpression, ident) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_ident(ident));
-                }
-                ExprKind::Index(main_expression, index_expression, span) => {
-                    try_visit!(vis.visit_expr(main_expression));
-                    try_visit!(vis.visit_expr(index_expression));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Range(start, end, _limit) => {
-                    visit_opt!(vis, visit_expr, start);
-                    visit_opt!(vis, visit_expr, end);
-                }
+                    visit_visitable!($($mut)? vis, constness, movability, capture_clause);
+                    let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body);
+                    try_visit!(vis.visit_fn(kind, *span, *id));
+                    visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span);
+                }
+                ExprKind::Block(block, opt_label) =>
+                    visit_visitable!($($mut)? vis, block, opt_label),
+                ExprKind::Gen(capt, body, kind, decl_span) =>
+                    visit_visitable!($($mut)? vis, capt, body, kind, decl_span),
+                ExprKind::Await(expr, span) | ExprKind::Use(expr, span) =>
+                    visit_visitable!($($mut)? vis, expr, span),
+                ExprKind::Assign(lhs, rhs, span) =>
+                    visit_visitable!($($mut)? vis, lhs, rhs, span),
+                ExprKind::AssignOp(op, lhs, rhs) =>
+                    visit_visitable!($($mut)? vis, op, lhs, rhs),
+                ExprKind::Field(subexpression, ident) =>
+                    visit_visitable!($($mut)? vis, subexpression, ident),
+                ExprKind::Index(main_expression, index_expression, span) =>
+                    visit_visitable!($($mut)? vis, main_expression, index_expression, span),
+                ExprKind::Range(start, end, limit) =>
+                    visit_visitable!($($mut)? vis, start, end, limit),
                 ExprKind::Underscore => {}
-                ExprKind::Path(maybe_qself, path) => {
-                    try_visit!(vis.visit_qself(maybe_qself));
-                    try_visit!(vis.visit_path(path));
-                }
-                ExprKind::Break(opt_label, opt_expr) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    visit_opt!(vis, visit_expr, opt_expr);
-                }
-                ExprKind::Continue(opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                }
-                ExprKind::Ret(optional_expression) => {
-                    visit_opt!(vis, visit_expr, optional_expression);
-                }
-                ExprKind::Yeet(optional_expression) => {
-                    visit_opt!(vis, visit_expr, optional_expression);
-                }
-                ExprKind::Become(expr) => try_visit!(vis.visit_expr(expr)),
-                ExprKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-                ExprKind::Paren(subexpression) => try_visit!(vis.visit_expr(subexpression)),
-                ExprKind::InlineAsm(asm) => try_visit!(vis.visit_inline_asm(asm)),
-                ExprKind::FormatArgs(f) => try_visit!(vis.visit_format_args(f)),
-                ExprKind::OffsetOf(container, fields) => {
-                    try_visit!(vis.visit_ty(container));
-                    walk_list!(vis, visit_ident, fields);
-                }
-                ExprKind::Yield(kind) => {
-                    match kind {
-                        YieldKind::Postfix(expr) => {
-                            try_visit!(vis.visit_expr(expr));
-                        }
-                        YieldKind::Prefix(expr) => {
-                            visit_opt!(vis, visit_expr, expr);
-                        }
-                    }
-                }
-                ExprKind::Try(subexpression) => try_visit!(vis.visit_expr(subexpression)),
-                ExprKind::TryBlock(body) => try_visit!(vis.visit_block(body)),
-                ExprKind::Lit(_token) => {}
-                ExprKind::IncludedBytes(_bytes) => {}
-                ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
-                    try_visit!(vis.visit_expr(expr));
-                    visit_opt!(vis, visit_ty, ty);
-                }
+                ExprKind::Path(maybe_qself, path) =>
+                    visit_visitable!($($mut)? vis, maybe_qself, path),
+                ExprKind::Break(opt_label, opt_expr) =>
+                    visit_visitable!($($mut)? vis, opt_label, opt_expr),
+                ExprKind::Continue(opt_label) =>
+                    visit_visitable!($($mut)? vis, opt_label),
+                ExprKind::Ret(optional_expression) | ExprKind::Yeet(optional_expression) =>
+                    visit_visitable!($($mut)? vis, optional_expression),
+                ExprKind::Become(expr) =>
+                    visit_visitable!($($mut)? vis, expr),
+                ExprKind::MacCall(mac) =>
+                    visit_visitable!($($mut)? vis, mac),
+                ExprKind::Paren(subexpression) =>
+                    visit_visitable!($($mut)? vis, subexpression),
+                ExprKind::InlineAsm(asm) =>
+                    visit_visitable!($($mut)? vis, asm),
+                ExprKind::FormatArgs(f) =>
+                    visit_visitable!($($mut)? vis, f),
+                ExprKind::OffsetOf(container, fields) =>
+                    visit_visitable!($($mut)? vis, container, fields),
+                ExprKind::Yield(kind) =>
+                    visit_visitable!($($mut)? vis, kind),
+                ExprKind::Try(subexpression) =>
+                    visit_visitable!($($mut)? vis, subexpression),
+                ExprKind::TryBlock(body) =>
+                    visit_visitable!($($mut)? vis, body),
+                ExprKind::Lit(token) =>
+                    visit_visitable!($($mut)? vis, token),
+                ExprKind::IncludedBytes(bytes) =>
+                    visit_visitable!($($mut)? vis, bytes),
+                ExprKind::UnsafeBinderCast(kind, expr, ty) =>
+                    visit_visitable!($($mut)? vis, kind, expr, ty),
                 ExprKind::Err(_guar) => {}
                 ExprKind::Dummy => {}
             }
 
             visit_span(vis, span)
-        }
-
-        pub fn walk_param<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, param: &$($lt)? $($mut)? Param) -> V::Result {
-            let Param { attrs, ty, pat, id, span, is_placeholder: _ } = param;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            try_visit!(vis.visit_ty(ty));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_arm<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, arm: &$($lt)? $($mut)? Arm) -> V::Result {
-            let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = arm;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            visit_opt!(vis, visit_expr, guard);
-            visit_opt!(vis, visit_expr, body);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_vis<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, visibility: &$($lt)? $($mut)? Visibility) -> V::Result {
-            let Visibility { kind, span, tokens: _ } = visibility;
-            match kind {
-                VisibilityKind::Restricted { path, id, shorthand: _ } => {
-                    try_visit!(visit_id(vis, id));
-                    try_visit!(vis.visit_path(path));
-                }
-                VisibilityKind::Public | VisibilityKind::Inherited => {}
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_attribute<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, attr: &$($lt)? $($mut)? Attribute) -> V::Result {
-            let Attribute { kind, id: _, style: _, span } = attr;
-            match kind {
-                AttrKind::Normal(normal) => {
-                    let NormalAttr { item, tokens: _ } = &$($mut)?**normal;
-                    let AttrItem { unsafety: _, path, args, tokens: _ } = item;
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(walk_attr_args(vis, args));
-                }
-                AttrKind::DocComment(_kind, _sym) => {}
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_attr_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, args: &$($lt)? $($mut)? AttrArgs) -> V::Result {
-            match args {
-                AttrArgs::Empty => {}
-                AttrArgs::Delimited(args) => try_visit!(visit_delim_args(vis, args)),
-                AttrArgs::Eq { eq_span, expr } => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, eq_span));
-                }
-            }
-            V::Result::output()
-        }
+        });
+
+        define_named_walk!($(($mut))? $Visitor$(<$lt>)?
+            pub fn walk_anon_const(AnonConst);
+            pub fn walk_arm(Arm);
+            //pub fn walk_assoc_item(AssocItem, _ctxt: AssocCtxt);
+            pub fn walk_assoc_item_constraint(AssocItemConstraint);
+            pub fn walk_attribute(Attribute);
+            pub fn walk_block(Block);
+            //pub fn walk_nested_use_tree((UseTree, NodeId));
+            pub fn walk_capture_by(CaptureBy);
+            pub fn walk_closure_binder(ClosureBinder);
+            pub fn walk_contract(FnContract);
+            pub fn walk_coroutine_kind(CoroutineKind);
+            pub fn walk_crate(Crate);
+            pub fn walk_expr(Expr);
+            pub fn walk_expr_field(ExprField);
+            pub fn walk_field_def(FieldDef);
+            pub fn walk_fn_decl(FnDecl);
+            pub fn walk_fn_header(FnHeader);
+            pub fn walk_fn_ret_ty(FnRetTy);
+            //pub fn walk_foreign_item(ForeignItem);
+            pub fn walk_foreign_mod(ForeignMod);
+            pub fn walk_format_args(FormatArgs);
+            pub fn walk_generic_arg(GenericArg);
+            pub fn walk_generic_args(GenericArgs);
+            pub fn walk_generic_param(GenericParam);
+            pub fn walk_generics(Generics);
+            pub fn walk_inline_asm(InlineAsm);
+            pub fn walk_inline_asm_sym(InlineAsmSym);
+            //pub fn walk_item(Item);
+            pub fn walk_label(Label);
+            pub fn walk_lifetime(Lifetime);
+            pub fn walk_local(Local);
+            pub fn walk_mac(MacCall);
+            pub fn walk_macro_def(MacroDef);
+            pub fn walk_param_bound(GenericBound);
+            pub fn walk_param(Param);
+            pub fn walk_pat_field(PatField);
+            pub fn walk_path(Path);
+            pub fn walk_path_segment(PathSegment);
+            pub fn walk_pat(Pat);
+            pub fn walk_poly_trait_ref(PolyTraitRef);
+            pub fn walk_precise_capturing_arg(PreciseCapturingArg);
+            pub fn walk_qself(QSelf);
+            pub fn walk_trait_ref(TraitRef);
+            pub fn walk_ty_pat(TyPat);
+            pub fn walk_ty(Ty);
+            pub fn walk_use_tree(UseTree);
+            pub fn walk_variant_data(VariantData);
+            pub fn walk_variant(Variant);
+            pub fn walk_vis(Visibility);
+            pub fn walk_where_predicate_kind(WherePredicateKind);
+            pub fn walk_where_predicate(WherePredicate);
+        );
     };
 }
 
@@ -1907,6 +1115,20 @@ common_visitor_and_walkers!(Visitor<'a>);
 macro_rules! generate_list_visit_fns {
     ($($name:ident, $Ty:ty, $visit_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
         $(
+            #[allow(unused_parens)]
+            impl<'a, V: Visitor<'a>> Visitable<'a, V> for ThinVec<$Ty> {
+                type Extra = ($($ParamTy),*);
+
+                #[inline]
+                fn visit(
+                    &'a self,
+                    visitor: &mut V,
+                    ($($param),*): Self::Extra,
+                ) -> V::Result {
+                    $name(visitor, self $(, $param)*)
+                }
+            }
+
             fn $name<'a, V: Visitor<'a>>(
                 vis: &mut V,
                 values: &'a ThinVec<$Ty>,
@@ -1937,18 +1159,9 @@ generate_list_visit_fns! {
     visit_arms, Arm, visit_arm;
 }
 
-#[expect(rustc::pass_by_value)] // needed for symmetry with mut_visit
-fn visit_nested_use_tree<'a, V: Visitor<'a>>(
-    vis: &mut V,
-    nested_tree: &'a UseTree,
-    &nested_id: &NodeId,
-) -> V::Result {
-    vis.visit_nested_use_tree(nested_tree, nested_id)
-}
-
 pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
     let Stmt { id, kind, span: _ } = statement;
-    try_visit!(visit_id(visitor, id));
+    try_visit!(visitor.visit_id(*id));
     match kind {
         StmtKind::Let(local) => try_visit!(visitor.visit_local(local)),
         StmtKind::Item(item) => try_visit!(visitor.visit_item(item)),
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 1006ea3ba10..101f5ccb7a4 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -21,6 +21,7 @@ mod symbols;
 mod try_from;
 mod type_foldable;
 mod type_visitable;
+mod visitable;
 
 // Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and
 // produces a `RustcVersion` literal containing that version (e.g.
@@ -101,6 +102,16 @@ decl_derive!(
     /// visited (and its type is not required to implement `TypeVisitable`).
     type_visitable::type_visitable_derive
 );
+decl_derive!(
+    [Walkable, attributes(visitable)] =>
+    /// Derives `Walkable` for the annotated `struct` or `enum` (`union` is not supported).
+    ///
+    /// Each field of the struct or enum variant will be visited in definition order, using the
+    /// `Walkable` implementation for its type. However, if a field of a struct or an enum
+    /// variant is annotated with `#[visitable(ignore)]` then that field will not be
+    /// visited (and its type is not required to implement `Walkable`).
+    visitable::visitable_derive
+);
 decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
 decl_derive!(
     [Diagnostic, attributes(
diff --git a/compiler/rustc_macros/src/visitable.rs b/compiler/rustc_macros/src/visitable.rs
new file mode 100644
index 00000000000..a7a82538eab
--- /dev/null
+++ b/compiler/rustc_macros/src/visitable.rs
@@ -0,0 +1,82 @@
+use quote::quote;
+use synstructure::BindingInfo;
+
+pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    let has_attr = |bind: &BindingInfo<'_>, name| {
+        let mut found = false;
+        bind.ast().attrs.iter().for_each(|attr| {
+            if !attr.path().is_ident("visitable") {
+                return;
+            }
+            let _ = attr.parse_nested_meta(|nested| {
+                if nested.path.is_ident(name) {
+                    found = true;
+                }
+                Ok(())
+            });
+        });
+        found
+    };
+
+    let get_attr = |bind: &BindingInfo<'_>, name: &str| {
+        let mut content = None;
+        bind.ast().attrs.iter().for_each(|attr| {
+            if !attr.path().is_ident("visitable") {
+                return;
+            }
+            let _ = attr.parse_nested_meta(|nested| {
+                if nested.path.is_ident(name) {
+                    let value = nested.value()?;
+                    let value = value.parse()?;
+                    content = Some(value);
+                }
+                Ok(())
+            });
+        });
+        content
+    };
+
+    s.add_bounds(synstructure::AddBounds::Generics);
+    s.bind_with(|_| synstructure::BindStyle::Ref);
+    let ref_visit = s.each(|bind| {
+        let extra = get_attr(bind, "extra").unwrap_or(quote! {});
+        if has_attr(bind, "ignore") {
+            quote! {}
+        } else {
+            quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) }
+        }
+    });
+
+    s.bind_with(|_| synstructure::BindStyle::RefMut);
+    let mut_visit = s.each(|bind| {
+        let extra = get_attr(bind, "extra").unwrap_or(quote! {});
+        if has_attr(bind, "ignore") {
+            quote! {}
+        } else {
+            quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) }
+        }
+    });
+
+    s.gen_impl(quote! {
+        gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self
+            where __V: crate::visit::Visitor<'__ast>,
+        {
+            fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result {
+                match *self { #ref_visit }
+                <__V::Result as rustc_ast_ir::visit::VisitorResult>::output()
+            }
+        }
+
+        gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self
+            where __V: crate::mut_visit::MutVisitor,
+        {
+            fn walk_mut(&mut self, __visitor: &mut __V) {
+                match *self { #mut_visit }
+            }
+        }
+    })
+}