//===--- ASTNodeTraverser.h - Traversal of AST nodes ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the AST traversal facilities.  Other users
// of this class may make use of the same traversal logic by inheriting it,
// similar to RecursiveASTVisitor.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_ASTNODETRAVERSER_H
#define LLVM_CLANG_AST_ASTNODETRAVERSER_H

#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/AttrVisitor.h"
#include "clang/AST/CommentVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/LocInfoType.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateArgumentVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/AST/TypeVisitor.h"
#include "llvm/Support/SaveAndRestore.h"

namespace clang {

class APValue;

/**

ASTNodeTraverser traverses the Clang AST for dumping purposes.

The `Derived::doGetNodeDelegate()` method is required to be an accessible member
which returns a reference of type `NodeDelegateType &` which implements the
following interface:

struct {
  template <typename Fn> void AddChild(Fn DoAddChild);
  template <typename Fn> void AddChild(StringRef Label, Fn DoAddChild);

  void Visit(const comments::Comment *C, const comments::FullComment *FC);
  void Visit(const Attr *A);
  void Visit(const TemplateArgument &TA, SourceRange R = {},
             const Decl *From = nullptr, StringRef Label = {});
  void Visit(const Stmt *Node);
  void Visit(const Type *T);
  void Visit(QualType T);
  void Visit(TypeLoc);
  void Visit(const Decl *D);
  void Visit(const CXXCtorInitializer *Init);
  void Visit(const OpenACCClause *C);
  void Visit(const OMPClause *C);
  void Visit(const BlockDecl::Capture &C);
  void Visit(const GenericSelectionExpr::ConstAssociation &A);
  void Visit(const concepts::Requirement *R);
  void Visit(const APValue &Value, QualType Ty);
};
*/
template <typename Derived, typename NodeDelegateType>
class ASTNodeTraverser
    : public ConstDeclVisitor<Derived>,
      public ConstStmtVisitor<Derived>,
      public comments::ConstCommentVisitor<Derived, void,
                                           const comments::FullComment *>,
      public TypeVisitor<Derived>,
      public TypeLocVisitor<Derived>,
      public ConstAttrVisitor<Derived>,
      public ConstTemplateArgumentVisitor<Derived> {

  /// Indicates whether we should trigger deserialization of nodes that had
  /// not already been loaded.
  bool Deserialize = false;

  /// Tracks whether we should dump TypeLocs etc.
  ///
  /// Detailed location information such as TypeLoc nodes is not usually
  /// included in the dump (too verbose).
  /// But when explicitly asked to dump a Loc node, we do so recursively,
  /// including e.g. FunctionTypeLoc => ParmVarDecl => TypeLoc.
  bool VisitLocs = false;

  TraversalKind Traversal = TraversalKind::TK_AsIs;

  NodeDelegateType &getNodeDelegate() {
    return getDerived().doGetNodeDelegate();
  }
  Derived &getDerived() { return *static_cast<Derived *>(this); }

public:
  void setDeserialize(bool D) { Deserialize = D; }
  bool getDeserialize() const { return Deserialize; }

  void SetTraversalKind(TraversalKind TK) { Traversal = TK; }
  TraversalKind GetTraversalKind() const { return Traversal; }

  void Visit(const Decl *D, bool VisitLocs = false) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource && D && D->isImplicit())
      return;

    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(D);
      if (!D)
        return;

      {
        llvm::SaveAndRestore RestoreVisitLocs(this->VisitLocs, VisitLocs);
        ConstDeclVisitor<Derived>::Visit(D);
      }

      for (const auto &A : D->attrs())
        Visit(A);

      if (const comments::FullComment *Comment =
              D->getASTContext().getLocalCommentForDeclUncached(D))
        Visit(Comment, Comment);

      // Decls within functions are visited by the body.
      if (!isa<FunctionDecl, ObjCMethodDecl, BlockDecl>(*D)) {
        if (Traversal != TK_AsIs) {
          if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
            auto SK = CTSD->getSpecializationKind();
            if (SK == TSK_ExplicitInstantiationDeclaration ||
                SK == TSK_ExplicitInstantiationDefinition)
              return;
          }
        }
        if (const auto *DC = dyn_cast<DeclContext>(D))
          dumpDeclContext(DC);
      }
    });
  }

  void Visit(const Stmt *Node, StringRef Label = {}) {
    getNodeDelegate().AddChild(Label, [=] {
      const Stmt *S = Node;

      if (auto *E = dyn_cast_or_null<Expr>(S)) {
        switch (Traversal) {
        case TK_AsIs:
          break;
        case TK_IgnoreUnlessSpelledInSource:
          S = E->IgnoreUnlessSpelledInSource();
          break;
        }
      }

      getNodeDelegate().Visit(S);

      if (!S) {
        return;
      }

      ConstStmtVisitor<Derived>::Visit(S);

      // Some statements have custom mechanisms for dumping their children.
      if (isa<DeclStmt, GenericSelectionExpr, RequiresExpr,
              OpenACCWaitConstruct, SYCLKernelCallStmt>(S))
        return;

      if (Traversal == TK_IgnoreUnlessSpelledInSource &&
          isa<LambdaExpr, CXXForRangeStmt, CallExpr,
              CXXRewrittenBinaryOperator>(S))
        return;

      for (const Stmt *SubStmt : S->children())
        Visit(SubStmt);
    });
  }

  void Visit(QualType T) {
    SplitQualType SQT = T.split();
    if (!SQT.Quals.hasQualifiers())
      return Visit(SQT.Ty);

    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(T);
      Visit(T.split().Ty);
    });
  }

  void Visit(const Type *T) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(T);
      if (!T)
        return;
      TypeVisitor<Derived>::Visit(T);

      QualType SingleStepDesugar =
          T->getLocallyUnqualifiedSingleStepDesugaredType();
      if (SingleStepDesugar != QualType(T, 0))
        Visit(SingleStepDesugar);
    });
  }

  void Visit(TypeLoc T) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(T);
      if (T.isNull())
        return;
      TypeLocVisitor<Derived>::Visit(T);
      if (auto Inner = T.getNextTypeLoc())
        Visit(Inner);
    });
  }

  void Visit(const Attr *A) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(A);
      ConstAttrVisitor<Derived>::Visit(A);
    });
  }

  void Visit(const CXXCtorInitializer *Init) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource && !Init->isWritten())
      return;
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(Init);
      Visit(Init->getInit());
    });
  }

  void Visit(const TemplateArgument &A, SourceRange R = {},
             const Decl *From = nullptr, const char *Label = nullptr) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(A, R, From, Label);
      ConstTemplateArgumentVisitor<Derived>::Visit(A);
    });
  }

  void Visit(const BlockDecl::Capture &C) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(C);
      if (C.hasCopyExpr())
        Visit(C.getCopyExpr());
    });
  }

  void Visit(const OpenACCClause *C) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(C);
      for (const auto *S : C->children())
        Visit(S);
    });
  }

  void Visit(const OMPClause *C) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(C);
      for (const auto *S : C->children())
        Visit(S);
    });
  }

  void Visit(const GenericSelectionExpr::ConstAssociation &A) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(A);
      if (const TypeSourceInfo *TSI = A.getTypeSourceInfo())
        Visit(TSI->getType());
      Visit(A.getAssociationExpr());
    });
  }

  void Visit(const concepts::Requirement *R) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(R);
      if (!R)
        return;
      if (auto *TR = dyn_cast<concepts::TypeRequirement>(R)) {
        if (!TR->isSubstitutionFailure())
          Visit(TR->getType()->getType().getTypePtr());
      } else if (auto *ER = dyn_cast<concepts::ExprRequirement>(R)) {
        if (!ER->isExprSubstitutionFailure())
          Visit(ER->getExpr());
        if (!ER->getReturnTypeRequirement().isEmpty())
          Visit(ER->getReturnTypeRequirement()
                    .getTypeConstraint()
                    ->getImmediatelyDeclaredConstraint());
      } else if (auto *NR = dyn_cast<concepts::NestedRequirement>(R)) {
        if (!NR->hasInvalidConstraint())
          Visit(NR->getConstraintExpr());
      }
    });
  }

  void Visit(const ConceptReference *R) {
    getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(R); });
  }

  void Visit(const APValue &Value, QualType Ty) {
    getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(Value, Ty); });
  }

  void Visit(const comments::Comment *C, const comments::FullComment *FC) {
    getNodeDelegate().AddChild([=] {
      getNodeDelegate().Visit(C, FC);
      if (!C) {
        return;
      }
      comments::ConstCommentVisitor<Derived, void,
                                    const comments::FullComment *>::visit(C,
                                                                          FC);
      for (comments::Comment::child_iterator I = C->child_begin(),
                                             E = C->child_end();
           I != E; ++I)
        Visit(*I, FC);
    });
  }

  void Visit(const DynTypedNode &N) {
    // FIXME: Improve this with a switch or a visitor pattern.
    if (const auto *D = N.get<Decl>())
      Visit(D);
    else if (const auto *S = N.get<Stmt>())
      Visit(S);
    else if (const auto *QT = N.get<QualType>())
      Visit(*QT);
    else if (const auto *T = N.get<Type>())
      Visit(T);
    else if (const auto *TL = N.get<TypeLoc>())
      Visit(*TL);
    else if (const auto *C = N.get<CXXCtorInitializer>())
      Visit(C);
    else if (const auto *C = N.get<OMPClause>())
      Visit(C);
    else if (const auto *T = N.get<TemplateArgument>())
      Visit(*T);
    else if (const auto *CR = N.get<ConceptReference>())
      Visit(CR);
  }

  void dumpDeclContext(const DeclContext *DC) {
    if (!DC)
      return;

    for (const auto *D : (Deserialize ? DC->decls() : DC->noload_decls()))
      Visit(D);
  }

  void dumpTemplateParameters(const TemplateParameterList *TPL) {
    if (!TPL)
      return;

    for (const auto &TP : *TPL)
      Visit(TP);

    if (const Expr *RC = TPL->getRequiresClause())
      Visit(RC);
  }

  void
  dumpASTTemplateArgumentListInfo(const ASTTemplateArgumentListInfo *TALI) {
    if (!TALI)
      return;

    for (const auto &TA : TALI->arguments())
      dumpTemplateArgumentLoc(TA);
  }

  void dumpTemplateArgumentLoc(const TemplateArgumentLoc &A,
                               const Decl *From = nullptr,
                               const char *Label = nullptr) {
    Visit(A.getArgument(), A.getSourceRange(), From, Label);
  }

  void dumpTemplateArgumentList(const TemplateArgumentList &TAL) {
    for (unsigned i = 0, e = TAL.size(); i < e; ++i)
      Visit(TAL[i]);
  }

  void dumpObjCTypeParamList(const ObjCTypeParamList *typeParams) {
    if (!typeParams)
      return;

    for (const auto &typeParam : *typeParams) {
      Visit(typeParam);
    }
  }

  void VisitComplexType(const ComplexType *T) { Visit(T->getElementType()); }
  void VisitLocInfoType(const LocInfoType *T) {
    Visit(T->getTypeSourceInfo()->getTypeLoc());
  }
  void VisitPointerType(const PointerType *T) { Visit(T->getPointeeType()); }
  void VisitBlockPointerType(const BlockPointerType *T) {
    Visit(T->getPointeeType());
  }
  void VisitReferenceType(const ReferenceType *T) {
    Visit(T->getPointeeType());
  }
  void VisitMemberPointerType(const MemberPointerType *T) {
    // FIXME: Provide a NestedNameSpecifier visitor.
    NestedNameSpecifier Qualifier = T->getQualifier();
    if (NestedNameSpecifier::Kind K = Qualifier.getKind();
        K == NestedNameSpecifier::Kind::Type)
      Visit(Qualifier.getAsType());
    if (T->isSugared())
      Visit(cast<MemberPointerType>(T->getCanonicalTypeUnqualified())
                ->getQualifier()
                .getAsType());
    Visit(T->getPointeeType());
  }
  void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); }
  void VisitVariableArrayType(const VariableArrayType *T) {
    VisitArrayType(T);
    Visit(T->getSizeExpr());
  }
  void VisitDependentSizedArrayType(const DependentSizedArrayType *T) {
    Visit(T->getElementType());
    Visit(T->getSizeExpr());
  }
  void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) {
    Visit(T->getElementType());
    Visit(T->getSizeExpr());
  }
  void VisitVectorType(const VectorType *T) { Visit(T->getElementType()); }
  void VisitFunctionType(const FunctionType *T) { Visit(T->getReturnType()); }
  void VisitFunctionProtoType(const FunctionProtoType *T) {
    VisitFunctionType(T);
    for (const QualType &PT : T->getParamTypes())
      Visit(PT);
  }
  void VisitTypeOfExprType(const TypeOfExprType *T) {
    Visit(T->getUnderlyingExpr());
  }
  void VisitDecltypeType(const DecltypeType *T) {
    Visit(T->getUnderlyingExpr());
  }

  void VisitPackIndexingType(const PackIndexingType *T) {
    Visit(T->getPattern());
    Visit(T->getIndexExpr());
  }

  void VisitUnaryTransformType(const UnaryTransformType *T) {
    Visit(T->getBaseType());
  }
  void VisitAttributedType(const AttributedType *T) {
    // FIXME: AttrKind
    if (T->getModifiedType() != T->getEquivalentType())
      Visit(T->getModifiedType());
  }
  void VisitBTFTagAttributedType(const BTFTagAttributedType *T) {
    Visit(T->getWrappedType());
  }
  void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) {
    QualType Contained = T->getContainedType();
    if (!Contained.isNull())
      Visit(Contained);
  }
  void VisitHLSLInlineSpirvType(const HLSLInlineSpirvType *T) {
    for (auto &Operand : T->getOperands()) {
      using SpirvOperandKind = SpirvOperand::SpirvOperandKind;

      switch (Operand.getKind()) {
      case SpirvOperandKind::ConstantId:
      case SpirvOperandKind::Literal:
        break;

      case SpirvOperandKind::TypeId:
        Visit(Operand.getResultType());
        break;

      default:
        llvm_unreachable("Invalid SpirvOperand kind!");
      }
    }
  }
  void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *) {}
  void
  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
    Visit(T->getArgumentPack());
  }
  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
    for (const auto &Arg : T->template_arguments())
      Visit(Arg);
  }
  void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) {
    Visit(T->getPointeeType());
  }
  void VisitAtomicType(const AtomicType *T) { Visit(T->getValueType()); }
  void VisitPipeType(const PipeType *T) { Visit(T->getElementType()); }
  void VisitAdjustedType(const AdjustedType *T) { Visit(T->getOriginalType()); }
  void VisitPackExpansionType(const PackExpansionType *T) {
    if (!T->isSugared())
      Visit(T->getPattern());
  }
  void VisitAutoType(const AutoType *T) {
    for (const auto &Arg : T->getTypeConstraintArguments())
      Visit(Arg);
  }
  // FIXME: ElaboratedType, DependentNameType,
  // DependentTemplateSpecializationType, ObjCObjectType

  // For TypeLocs, we automatically visit the inner type loc (pointee type etc).
  // We must explicitly visit other lexically-nested nodes.
  void VisitFunctionProtoTypeLoc(FunctionProtoTypeLoc TL) {
    TypeLocVisitor<Derived>::VisitFunctionTypeLoc(TL);
    for (const auto *Param : TL.getParams())
      Visit(Param, /*VisitTypeLocs=*/true);
  }
  void VisitAutoTypeLoc(AutoTypeLoc TL) {
    if (const auto *CR = TL.getConceptReference()) {
      if (auto *Args = CR->getTemplateArgsAsWritten())
        for (const auto &Arg : Args->arguments())
          dumpTemplateArgumentLoc(Arg);
    }
  }
  void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
    // FIXME: Provide NestedNamespecifierLoc visitor.
    Visit(TL.getQualifierLoc().castAsTypeLoc());
  }
  void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
    Visit(TL.getSizeExpr());
  }
  void VisitDependentSizedArrayTypeLoc(DependentSizedArrayTypeLoc TL) {
    Visit(TL.getSizeExpr());
  }
  void VisitDependentSizedExtVectorTypeLoc(DependentSizedExtVectorTypeLoc TL) {
    Visit(cast<DependentSizedExtVectorType>(TL.getType())->getSizeExpr());
  }
  void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
    Visit(TL.getUnderlyingExpr());
  }
  void VisitDecltypeType(DecltypeType TL) {
    Visit(TL.getUnderlyingExpr());
  }
  void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
    for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I)
      dumpTemplateArgumentLoc(TL.getArgLoc(I));
  }

  void VisitTypedefDecl(const TypedefDecl *D) { Visit(D->getUnderlyingType()); }

  void VisitEnumConstantDecl(const EnumConstantDecl *D) {
    if (const Expr *Init = D->getInitExpr())
      Visit(Init);
  }

  void VisitFunctionDecl(const FunctionDecl *D) {
    if (FunctionTemplateSpecializationInfo *FTSI =
            D->getTemplateSpecializationInfo())
      dumpTemplateArgumentList(*FTSI->TemplateArguments);
    else if (DependentFunctionTemplateSpecializationInfo *DFTSI =
                 D->getDependentSpecializationInfo())
      dumpASTTemplateArgumentListInfo(DFTSI->TemplateArgumentsAsWritten);

    if (D->param_begin())
      for (const auto *Parameter : D->parameters())
        Visit(Parameter);

    if (const AssociatedConstraint &TRC = D->getTrailingRequiresClause())
      Visit(TRC.ConstraintExpr);

    if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isDefaulted())
      return;

    if (const auto *C = dyn_cast<CXXConstructorDecl>(D))
      for (const auto *I : C->inits())
        Visit(I);

    if (D->doesThisDeclarationHaveABody())
      Visit(D->getBody());
  }

  void VisitFieldDecl(const FieldDecl *D) {
    if (D->isBitField())
      Visit(D->getBitWidth());
    if (Expr *Init = D->getInClassInitializer())
      Visit(Init);
  }

  void VisitVarDecl(const VarDecl *D) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
      return;

    if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
      Visit(TSI->getTypeLoc());
    if (D->hasInit())
      Visit(D->getInit());
  }

  void VisitDecompositionDecl(const DecompositionDecl *D) {
    VisitVarDecl(D);
    for (const auto *B : D->bindings())
      Visit(B);
  }

  void VisitBindingDecl(const BindingDecl *D) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource)
      return;

    if (const auto *V = D->getHoldingVar())
      Visit(V);

    if (const auto *E = D->getBinding())
      Visit(E);
  }

  void VisitFileScopeAsmDecl(const FileScopeAsmDecl *D) {
    Visit(D->getAsmStringExpr());
  }

  void VisitTopLevelStmtDecl(const TopLevelStmtDecl *D) { Visit(D->getStmt()); }

  void VisitOutlinedFunctionDecl(const OutlinedFunctionDecl *D) {
    for (const ImplicitParamDecl *Parameter : D->parameters())
      Visit(Parameter);
    Visit(D->getBody());
  }

  void VisitCapturedDecl(const CapturedDecl *D) { Visit(D->getBody()); }

  void VisitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D) {
    for (const auto *E : D->varlist())
      Visit(E);
  }

  void VisitOMPGroupPrivateDecl(const OMPGroupPrivateDecl *D) {
    for (const auto *E : D->varlist())
      Visit(E);
  }

  void VisitOMPDeclareReductionDecl(const OMPDeclareReductionDecl *D) {
    Visit(D->getCombiner());
    if (const auto *Initializer = D->getInitializer())
      Visit(Initializer);
  }

  void VisitOMPDeclareMapperDecl(const OMPDeclareMapperDecl *D) {
    for (const auto *C : D->clauselists())
      Visit(C);
  }

  void VisitOMPCapturedExprDecl(const OMPCapturedExprDecl *D) {
    Visit(D->getInit());
  }

  void VisitOMPAllocateDecl(const OMPAllocateDecl *D) {
    for (const auto *E : D->varlist())
      Visit(E);
    for (const auto *C : D->clauselists())
      Visit(C);
  }

  template <typename SpecializationDecl>
  void dumpTemplateDeclSpecialization(const SpecializationDecl *D) {
    for (const auto *Redecl : D->redecls())
      Visit(cast<SpecializationDecl>(Redecl));
  }

  template <typename TemplateDecl>
  void dumpTemplateDecl(const TemplateDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());

    Visit(D->getTemplatedDecl());

    if (Traversal == TK_AsIs) {
      for (const auto *Child : D->specializations())
        dumpTemplateDeclSpecialization(Child);
    }
  }

  void VisitTypeAliasDecl(const TypeAliasDecl *D) {
    Visit(D->getUnderlyingType());
  }

  void VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());
    Visit(D->getTemplatedDecl());
  }

  void VisitStaticAssertDecl(const StaticAssertDecl *D) {
    Visit(D->getAssertExpr());
    Visit(D->getMessage());
  }

  void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
    dumpTemplateDecl(D);
  }

  void VisitClassTemplateDecl(const ClassTemplateDecl *D) {
    dumpTemplateDecl(D);
  }

  void VisitClassTemplateSpecializationDecl(
      const ClassTemplateSpecializationDecl *D) {
    dumpTemplateArgumentList(D->getTemplateArgs());
  }

  void VisitClassTemplatePartialSpecializationDecl(
      const ClassTemplatePartialSpecializationDecl *D) {
    VisitClassTemplateSpecializationDecl(D);
    dumpTemplateParameters(D->getTemplateParameters());
  }

  void VisitVarTemplateDecl(const VarTemplateDecl *D) { dumpTemplateDecl(D); }

  void VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());
  }

  void
  VisitVarTemplateSpecializationDecl(const VarTemplateSpecializationDecl *D) {
    dumpTemplateArgumentList(D->getTemplateArgs());
    VisitVarDecl(D);
  }

  void VisitVarTemplatePartialSpecializationDecl(
      const VarTemplatePartialSpecializationDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());
    VisitVarTemplateSpecializationDecl(D);
  }

  void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
    if (const auto *TC = D->getTypeConstraint())
      Visit(TC->getImmediatelyDeclaredConstraint());
    if (D->hasDefaultArgument())
      Visit(D->getDefaultArgument().getArgument(), SourceRange(),
            D->getDefaultArgStorage().getInheritedFrom(),
            D->defaultArgumentWasInherited() ? "inherited from" : "previous");
  }

  void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
    if (const auto *E = D->getPlaceholderTypeConstraint())
      Visit(E);
    if (D->hasDefaultArgument())
      dumpTemplateArgumentLoc(
          D->getDefaultArgument(), D->getDefaultArgStorage().getInheritedFrom(),
          D->defaultArgumentWasInherited() ? "inherited from" : "previous");
  }

  void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());
    if (D->hasDefaultArgument())
      dumpTemplateArgumentLoc(
          D->getDefaultArgument(), D->getDefaultArgStorage().getInheritedFrom(),
          D->defaultArgumentWasInherited() ? "inherited from" : "previous");
  }

  void VisitConceptDecl(const ConceptDecl *D) {
    dumpTemplateParameters(D->getTemplateParameters());
    Visit(D->getConstraintExpr());
  }

  void VisitImplicitConceptSpecializationDecl(
      const ImplicitConceptSpecializationDecl *CSD) {
    for (const TemplateArgument &Arg : CSD->getTemplateArguments())
      Visit(Arg);
  }

  void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *CSE) {
    Visit(CSE->getSpecializationDecl());
    if (CSE->hasExplicitTemplateArgs())
      for (const auto &ArgLoc : CSE->getTemplateArgsAsWritten()->arguments())
        dumpTemplateArgumentLoc(ArgLoc);
  }

  void VisitUsingShadowDecl(const UsingShadowDecl *D) {
    Visit(D->getTargetDecl());
  }

  void VisitFriendDecl(const FriendDecl *D) {
    if (D->getFriendType()) {
      // Traverse any CXXRecordDecl owned by this type, since
      // it will not be in the parent context:
      if (auto *TT = D->getFriendType()->getType()->getAs<TagType>())
        if (TT->isTagOwned())
          Visit(TT->getDecl());
    } else {
      Visit(D->getFriendDecl());
    }
  }

  void VisitObjCMethodDecl(const ObjCMethodDecl *D) {
    if (D->isThisDeclarationADefinition())
      dumpDeclContext(D);
    else
      for (const ParmVarDecl *Parameter : D->parameters())
        Visit(Parameter);

    if (D->hasBody())
      Visit(D->getBody());
  }

  void VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
    dumpObjCTypeParamList(D->getTypeParamList());
  }

  void VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
    dumpObjCTypeParamList(D->getTypeParamListAsWritten());
  }

  void VisitObjCImplementationDecl(const ObjCImplementationDecl *D) {
    for (const auto &I : D->inits())
      Visit(I);
  }

  void VisitBlockDecl(const BlockDecl *D) {
    for (const auto &I : D->parameters())
      Visit(I);

    for (const auto &I : D->captures())
      Visit(I);
    Visit(D->getBody());
  }

  void VisitDeclStmt(const DeclStmt *Node) {
    for (const auto &D : Node->decls())
      Visit(D);
  }

  void VisitAttributedStmt(const AttributedStmt *Node) {
    for (const auto *A : Node->getAttrs())
      Visit(A);
  }

  void VisitLabelStmt(const LabelStmt *Node) {
    if (Node->getDecl()->hasAttrs()) {
      for (const auto *A : Node->getDecl()->getAttrs())
        Visit(A);
    }
  }

  void VisitCXXCatchStmt(const CXXCatchStmt *Node) {
    Visit(Node->getExceptionDecl());
  }

  void VisitCapturedStmt(const CapturedStmt *Node) {
    Visit(Node->getCapturedDecl());
  }

  void VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *Node) {
    Visit(Node->getOriginalStmt());
    if (Traversal != TK_IgnoreUnlessSpelledInSource)
      Visit(Node->getOutlinedFunctionDecl());
  }

  void VisitOMPExecutableDirective(const OMPExecutableDirective *Node) {
    for (const auto *C : Node->clauses())
      Visit(C);
  }

  void VisitOpenACCConstructStmt(const OpenACCConstructStmt *Node) {
    for (const auto *C : Node->clauses())
      Visit(C);
  }

  void VisitOpenACCWaitConstruct(const OpenACCWaitConstruct *Node) {
    // Needs custom child checking to put clauses AFTER the children, which are
    // the expressions in the 'wait' construct. Others likely need this as well,
    // and might need to do the associated statement after it.
    for (const Stmt *S : Node->children())
      Visit(S);
    for (const auto *C : Node->clauses())
      Visit(C);
  }

  void VisitInitListExpr(const InitListExpr *ILE) {
    if (auto *Filler = ILE->getArrayFiller()) {
      Visit(Filler, "array_filler");
    }
  }

  void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE) {
    if (auto *Filler = PLIE->getArrayFiller()) {
      Visit(Filler, "array_filler");
    }
  }

  void VisitBlockExpr(const BlockExpr *Node) { Visit(Node->getBlockDecl()); }

  void VisitOpaqueValueExpr(const OpaqueValueExpr *Node) {
    if (Expr *Source = Node->getSourceExpr())
      Visit(Source);
  }

  void VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
    if (E->isExprPredicate()) {
      Visit(E->getControllingExpr());
      Visit(E->getControllingExpr()->getType()); // FIXME: remove
    } else
      Visit(E->getControllingType()->getType());

    for (const auto Assoc : E->associations()) {
      Visit(Assoc);
    }
  }

  void VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *E) {
    if (E->hasExplicitTemplateArgs())
      for (auto Arg : E->template_arguments())
        Visit(Arg.getArgument());
  }

  void VisitRequiresExpr(const RequiresExpr *E) {
    for (auto *D : E->getLocalParameters())
      Visit(D);
    for (auto *R : E->getRequirements())
      Visit(R);
  }

  void VisitTypeTraitExpr(const TypeTraitExpr *E) {
    // Argument types are not children of the TypeTraitExpr.
    for (auto *A : E->getArgs())
      Visit(A->getType());
  }

  void VisitLambdaExpr(const LambdaExpr *Node) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource) {
      for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {
        const auto *C = Node->capture_begin() + I;
        if (!C->isExplicit())
          continue;
        if (Node->isInitCapture(C))
          Visit(C->getCapturedVar());
        else
          Visit(Node->capture_init_begin()[I]);
      }
      dumpTemplateParameters(Node->getTemplateParameterList());
      for (const auto *P : Node->getCallOperator()->parameters())
        Visit(P);
      Visit(Node->getBody());
    } else {
      return Visit(Node->getLambdaClass());
    }
  }

  void VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {
    if (Node->isPartiallySubstituted())
      for (const auto &A : Node->getPartialArguments())
        Visit(A);
  }

  void VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) {
    Visit(E->getParameter());
  }
  void VisitSubstNonTypeTemplateParmPackExpr(
      const SubstNonTypeTemplateParmPackExpr *E) {
    Visit(E->getParameterPack());
    Visit(E->getArgumentPack());
  }

  void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node) {
    if (const VarDecl *CatchParam = Node->getCatchParamDecl())
      Visit(CatchParam);
  }

  void VisitCXXForRangeStmt(const CXXForRangeStmt *Node) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource) {
      Visit(Node->getInit());
      Visit(Node->getLoopVariable());
      Visit(Node->getRangeInit());
      Visit(Node->getBody());
    }
  }

  void VisitCallExpr(const CallExpr *Node) {
    for (const auto *Child :
         make_filter_range(Node->children(), [this](const Stmt *Child) {
           if (Traversal != TK_IgnoreUnlessSpelledInSource)
             return false;
           return !isa<CXXDefaultArgExpr>(Child);
         })) {
      Visit(Child);
    }
  }

  void VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *Node) {
    if (Traversal == TK_IgnoreUnlessSpelledInSource) {
      Visit(Node->getLHS());
      Visit(Node->getRHS());
    } else {
      ConstStmtVisitor<Derived>::VisitCXXRewrittenBinaryOperator(Node);
    }
  }

  void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
    Visit(TA.getAsExpr());
  }

  void VisitTypeTemplateArgument(const TemplateArgument &TA) {
    Visit(TA.getAsType());
  }

  void VisitPackTemplateArgument(const TemplateArgument &TA) {
    for (const auto &TArg : TA.pack_elements())
      Visit(TArg);
  }

  void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node) {
    Visit(Node->getExpr());
  }

  void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node) {
    Visit(Node->getExpr());
  }

  // Implements Visit methods for Attrs.
#include "clang/AST/AttrNodeTraverse.inc"
};

} // namespace clang

#endif // LLVM_CLANG_AST_ASTNODETRAVERSER_H
