//! AST walker. Each overridden visit method has full control over what
//! happens with its node, it can do its own traversal of the node's children,
//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
//! deeper traversal by doing nothing.
//!
//! Note: it is an important invariant that the default visitor walks the body
//! of a function in "execution order" (more concretely, reverse post-order
//! with respect to the CFG implied by the AST), meaning that if AST node A may
//! execute before AST node B, then A is visited first. The borrow checker in
//! particular relies on this property.
//!
//! Note: walking an AST before macro expansion is probably a bad idea. For
//! instance, a walker looking for item names in a module will miss all of
//! those that are created by the expansion of a macro.

pub use rustc_ast_ir::visit::VisitorResult;
pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
use rustc_span::{Ident, Span, Spanned, Symbol};
use thin_vec::ThinVec;

use crate::ast::*;
use crate::tokenstream::DelimSpan;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AssocCtxt {
    Trait,
    Impl { of_trait: bool },
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FnCtxt {
    Free,
    Foreign,
    Assoc(AssocCtxt),
}

#[derive(Copy, Clone, Debug)]
pub enum BoundKind {
    /// Trait bounds in generics bounds and type/trait alias.
    /// E.g., `<T: Bound>`, `type A: Bound`, or `where T: Bound`.
    Bound,

    /// Trait bounds in `impl` type.
    /// E.g., `type Foo = impl Bound1 + Bound2 + Bound3`.
    Impl,

    /// Trait bounds in trait object type.
    /// E.g., `dyn Bound1 + Bound2 + Bound3`.
    TraitObject,

    /// Super traits of a trait.
    /// E.g., `trait A: B`
    SuperTraits,
}
impl BoundKind {
    pub fn descr(self) -> &'static str {
        match self {
            BoundKind::Bound => "bounds",
            BoundKind::Impl => "`impl Trait`",
            BoundKind::TraitObject => "`dyn` trait object bounds",
            BoundKind::SuperTraits => "supertrait bounds",
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub enum LifetimeCtxt {
    /// Appears in a reference type.
    Ref,
    /// Appears as a bound on a type or another lifetime.
    Bound,
    /// Appears as a generic argument.
    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 Box<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>)?) => {
        $(${ignore($lt)}
            #[derive(Copy, Clone)]
        )?
        #[derive(Debug)]
        pub enum FnKind<'a> {
            /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
            Fn(FnCtxt, &'a $($mut)? Visibility, &'a $($mut)? Fn),

            /// E.g., `|x, y| body`.
            Closure(&'a $($mut)? ClosureBinder, &'a $($mut)? Option<CoroutineKind>, &'a $($mut)? Box<FnDecl>, &'a $($mut)? Box<Expr>),
        }

        impl<'a> FnKind<'a> {
            pub fn header(&'a $($mut)? self) -> Option<&'a $($mut)? FnHeader> {
                match *self {
                    FnKind::Fn(_, _, Fn { sig, .. }) => Some(&$($mut)? sig.header),
                    FnKind::Closure(..) => None,
                }
            }

            pub fn ident(&'a $($mut)? self) -> Option<&'a $($mut)? Ident> {
                match self {
                    FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
                    _ => None,
                }
            }

            pub fn decl(&'a $($mut)? self) -> &'a $($mut)? FnDecl {
                match self {
                    FnKind::Fn(_, _, Fn { sig, .. }) => &$($mut)? sig.decl,
                    FnKind::Closure(_, _, decl, _) => decl,
                }
            }

            pub fn ctxt(&self) -> Option<FnCtxt> {
                match self {
                    FnKind::Fn(ctxt, ..) => Some(*ctxt),
                    FnKind::Closure(..) => None,
                }
            }
        }

        // 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,
            EarlyParsedAttribute,
            Movability,
            Mutability,
            Pinnedness,
            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<Pat>,
            ThinVec<Box<Ty>>,
            ThinVec<TyPat>,
            ThinVec<EiiImpl>,
        );

        // 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,
            MgcaDisambiguation,
            BorrowKind,
            BoundAsyncness,
            BoundConstness,
            BoundPolarity,
            ByRef,
            Closure,
            Const,
            ConstBlockItem,
            ConstItem,
            ConstItemRhsKind,
            Defaultness,
            Delegation,
            DelegationMac,
            DelegationSuffixes,
            DelimArgs,
            DelimSpan,
            EnumDef,
            Extern,
            ForLoopKind,
            FormatArgPosition,
            FormatArgsPiece,
            FormatArgument,
            FormatArgumentKind,
            FormatArguments,
            FormatPlaceholder,
            GenericParamKind,
            Guard,
            Impl,
            ImplPolarity,
            Inline,
            InlineAsmOperand,
            InlineAsmRegOrRegClass,
            InlineAsmTemplatePiece,
            IsAuto,
            LocalKind,
            MacCallStmt,
            MacStmtStyle,
            MatchKind,
            MethodCall,
            ModKind,
            ModSpans,
            MutTy,
            NormalAttr,
            AttrItemKind,
            Parens,
            ParenthesizedArgs,
            PatFieldsRest,
            PatKind,
            RangeEnd,
            RangeSyntax,
            Recovered,
            RestrictionKind,
            Safety,
            StaticItem,
            StrLit,
            StrStyle,
            StructExpr,
            StructRest,
            Term,
            Trait,
            TraitBoundModifiers,
            TraitObjectSyntax,
            TyAlias,
            TyKind,
            TyPatKind,
            UnOp,
            UnsafeBinderTy,
            UnsafeSource,
            UseTreeKind,
            VisibilityKind,
            WhereBoundPredicate,
            WhereClause,
            WhereEqPredicate,
            WhereRegionPredicate,
            YieldKind,
            EiiDecl,
            EiiImpl,
        );

        /// 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;
        #[doc = concat!(" e.g., the `visit_item` method by default calls `visit"$(, "_", stringify!($mut))?, "::walk_item`.")]
        ///
        /// If you want to ensure that your code handles every variant
        /// explicitly, you need to override each method. (And you also need
        /// to monitor future changes to this trait in case a new method with a
        /// new default implementation gets introduced.)
        ///
        /// Every `walk_*` method uses deconstruction to access fields of structs and
        /// enums. This will result in a compile error if a field is added, which makes
        /// it more likely the appropriate visit call will be added for it.
        pub trait $Visitor<$($lt)?> : Sized $(${ignore($mut)} + MutVisitorResult<Result = ()>)? {
            $(
                ${ignore($lt)}
                /// The result type of the `visit_*` methods. Can be either `()`,
                /// or `ControlFlow<T>`.
                type Result: VisitorResult = ();
            )?

            // Methods in this trait have one of three forms, with the last two forms
            // only occurring on `MutVisitor`:
            //
            //   fn visit_t(&mut self, t: &mut T);                      // common
            //   fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>;    // rare
            //   fn filter_map_t(&mut self, t: T) -> Option<T>;         // rarest
            //
            // When writing these methods, it is better to use destructuring like this:
            //
            //   fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
            //       visit_a(a);
            //       visit_b(b);
            //   }
            //
            // than to use field access like this:
            //
            //   fn visit_abc(&mut self, abc: &mut ABC) {
            //       visit_a(&mut abc.a);
            //       visit_b(&mut abc.b);
            //       // ignore abc.c
            //   }
            //
            // As well as being more concise, the former is explicit about which fields
            // are skipped. Furthermore, if a new field is added, the destructuring
            // version will cause a compile error, which is good. In comparison, the
            // 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)
            }

            // 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_impl_restriction(ImplRestriction);
                //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_mut_restriction(MutRestriction);
                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`.
            /// It can be removed once that feature is stabilized.
            fn visit_method_receiver_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
                self.visit_expr(ex)
            }

            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_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_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.
            fn visit_fn(
                &mut self,
                fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>,
                _: &AttrVec,
                _: Span,
                _: NodeId,
            ) -> Self::Result {
                walk_fn(self, fk)
            }

            // (non-mut) `Visitor`-only methods
            $(
                fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result {
                    walk_stmt(self, s)
                }

                fn visit_nested_use_tree(&mut self, use_tree: &$lt UseTree, id: NodeId) -> Self::Result {
                    try_visit!(self.visit_id(id));
                    self.visit_use_tree(use_tree)
                }
            )?

            // `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: Box<ForeignItem>) -> SmallVec<[Box<ForeignItem>; 1]> {
                    walk_flat_map_foreign_item(self, ni)
                }

                fn flat_map_item(&mut self, i: Box<Item>) -> SmallVec<[Box<Item>; 1]> {
                    walk_flat_map_item(self, i)
                }

                fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
                    walk_flat_map_field_def(self, fd)
                }

                fn flat_map_assoc_item(
                    &mut self,
                    i: Box<AssocItem>,
                    ctxt: AssocCtxt,
                ) -> SmallVec<[Box<AssocItem>; 1]> {
                    walk_flat_map_assoc_item(self, i, ctxt)
                }

                fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
                    walk_flat_map_stmt(self, s)
                }

                fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
                    walk_flat_map_arm(self, arm)
                }

                fn filter_map_expr(&mut self, e: Box<Expr>) -> Option<Box<Expr>> {
                    walk_filter_map_expr(self, e)
                }

                fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
                    walk_flat_map_variant(self, v)
                }

                fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
                    walk_flat_map_param(self, param)
                }

                fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
                    walk_flat_map_generic_param(self, param)
                }

                fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
                    walk_flat_map_expr_field(self, f)
                }

                fn flat_map_where_predicate(
                    &mut self,
                    where_predicate: WherePredicate,
                ) -> SmallVec<[WherePredicate; 1]> {
                    walk_flat_map_where_predicate(self, where_predicate)
                }

                fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
                    walk_flat_map_pat_field(self, fp)
                }
            )?
        }

        pub trait WalkItemKind {
            type Ctxt;
            fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
                &$($lt)? $($mut)? self,
                attrs: &AttrVec,
                span: Span,
                id: NodeId,
                visibility: &$($lt)? $($mut)? Visibility,
                ctxt: Self::Ctxt,
                vis: &mut V,
            ) -> V::Result;
        }

        // This is only used by the MutVisitor. We include this symmetry here to make writing other
        // functions easier.
        $(${ignore($lt)}
            #[expect(unused, rustc::disallowed_pass_by_ref)]
            #[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()
        }

        $(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));
            }
            V::Result::output()
        });)?
        $(impl_visitable_list!(<$mut> ThinVec<(UseTree, NodeId)>,);)?

        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;
            visit_visitable!($($mut)? visitor, id, attrs, vis);
            try_visit!(kind.walk(attrs, *span, *id, vis, ctxt, visitor));
            visit_visitable!($($mut)? visitor, span);
            V::Result::output()
        }

        // 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_inner(visitor, item, ())
        }

        // 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)? Item<K>,
            ctxt: AssocCtxt,
        ) -> V::Result {
            walk_item_inner(visitor, item, ctxt)
        }

        impl WalkItemKind for ItemKind {
            type Ctxt = ();
            fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
                &$($lt)? $($mut)? self,
                attrs: &AttrVec,
                span: Span,
                id: NodeId,
                visibility: &$($lt)? $($mut)? Visibility,
                _ctxt: Self::Ctxt,
                vis: &mut V,
            ) -> V::Result {
                match self {
                    ItemKind::Fn(func) => {
                        let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func);
                        try_visit!(vis.visit_fn(kind, attrs, 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::ConstBlock(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) =>
                        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(TraitAlias { constness, ident, generics, bounds}) => {
                        visit_visitable!($($mut)? vis, constness, 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()
            }
        }

        impl WalkItemKind for AssocItemKind {
            type Ctxt = AssocCtxt;
            fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
                &$($lt)? $($mut)? self,
                attrs: &AttrVec,
                span: Span,
                id: NodeId,
                visibility: &$($lt)? $($mut)? Visibility,
                ctxt: Self::Ctxt,
                vis: &mut V,
            ) -> V::Result {
                match self {
                    AssocItemKind::Const(item) =>
                        visit_visitable!($($mut)? vis, item),
                    AssocItemKind::Fn(func) => {
                        let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func);
                        try_visit!(vis.visit_fn(kind, attrs, 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()
            }
        }

        impl WalkItemKind for ForeignItemKind {
            type Ctxt = ();
            fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
                &$($lt)? $($mut)? self,
                attrs: &AttrVec,
                span: Span,
                id: NodeId,
                visibility: &$($lt)? $($mut)? Visibility,
                _ctxt: Self::Ctxt,
                vis: &mut V,
            ) -> V::Result {
                match self {
                    ForeignItemKind::Static(item) =>
                        visit_visitable!($($mut)? vis, item),
                    ForeignItemKind::Fn(func) => {
                        let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func);
                        try_visit!(vis.visit_fn(kind, attrs, span, id))
                    }
                    ForeignItemKind::TyAlias(alias) =>
                        visit_visitable!($($mut)? vis, alias),
                    ForeignItemKind::MacCall(mac) =>
                        visit_visitable!($($mut)? vis, mac),
                }
                V::Result::output()
            }
        }

        pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)? $(${ignore($mut)} '_)?>) -> V::Result {
            match kind {
                FnKind::Fn(
                    _ctxt,
                    // Visibility is visited as a part of the item.
                    _vis,
                    Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
                ) => {
                    let FnSig { header, decl, span } = sig;
                    visit_visitable!($($mut)? vis,
                        defaultness, ident, header, generics, decl,
                        contract, body, span, define_opaque, eii_impls
                    );
                }
                FnKind::Closure(binder, coroutine_kind, decl, body) =>
                    visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
            }
            V::Result::output()
        }

        impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| {
            let Impl { generics, of_trait, self_ty, items, constness: _ } = self;
            try_visit!(vis.visit_generics(generics));
            if let Some(of_trait) = of_trait {
                let TraitImplHeader { defaultness, safety, polarity, trait_ref } = of_trait;
                visit_visitable!($($mut)? vis, defaultness, safety, polarity, trait_ref);
            }
            try_visit!(vis.visit_ty(self_ty));
            visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() });
            V::Result::output()
        });

        // 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()
        });

        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) =>
                    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(Closure {
                    binder,
                    capture_clause,
                    coroutine_kind,
                    constness,
                    movability,
                    fn_decl,
                    body,
                    fn_decl_span,
                    fn_arg_span,
                }) => {
                    visit_visitable!($($mut)? vis, constness, movability, capture_clause);
                    let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body);
                    try_visit!(vis.visit_fn(kind, attrs, *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::Move(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) =>
                    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, optional_type) =>
                    visit_visitable!($($mut)? vis, body, optional_type),
                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)
        });

        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_impl_restriction(ImplRestriction);
            //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_mut_restriction(MutRestriction);
            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);
        );
    };
}

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>,
                $(
                    $param: $ParamTy,
                )*
            ) -> V::Result {
                walk_list!(vis, $visit_fn, values$(,$param)*);
                V::Result::output()
            }
        )+
    }
}

generate_list_visit_fns! {
    visit_items, Box<Item>, visit_item;
    visit_foreign_items, Box<ForeignItem>, visit_foreign_item;
    visit_generic_params, GenericParam, visit_generic_param;
    visit_stmts, Stmt, visit_stmt;
    visit_exprs, Box<Expr>, visit_expr;
    visit_expr_fields, ExprField, visit_expr_field;
    visit_pat_fields, PatField, visit_pat_field;
    visit_variants, Variant, visit_variant;
    visit_assoc_items, Box<AssocItem>, visit_assoc_item, ctxt: AssocCtxt;
    visit_where_predicates, WherePredicate, visit_where_predicate;
    visit_params, Param, visit_param;
    visit_field_defs, FieldDef, visit_field_def;
    visit_arms, Arm, visit_arm;
}

pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
    let Stmt { id, kind, span: _ } = statement;
    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)),
        StmtKind::Expr(expr) | StmtKind::Semi(expr) => try_visit!(visitor.visit_expr(expr)),
        StmtKind::Empty => {}
        StmtKind::MacCall(mac) => {
            let MacCallStmt { mac, attrs, style: _, tokens: _ } = &**mac;
            walk_list!(visitor, visit_attribute, attrs);
            try_visit!(visitor.visit_mac_call(mac));
        }
    }
    V::Result::output()
}
