//===--- DynamicRecursiveASTVisitor.h - Virtual AST Visitor -----*- C++ -*-===//
//
// 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 defines the DynamicRecursiveASTVisitor interface, which acts
//  identically to RecursiveASTVisitor, except that it uses virtual dispatch
//  instead of CRTP, which greatly improves compile times and binary size.
//
//  Prefer to use this over RecursiveASTVisitor whenever possible.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H
#define LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H

#include "clang/AST/Attr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/TypeLoc.h"

namespace clang {
class ASTContext;

/// Recursive AST visitor that supports extension via dynamic dispatch.
///
/// Like RecursiveASTVisitor, this class allows for traversal of arbitrarily
/// complex ASTs. The main difference is that this uses virtual functions
/// instead of CRTP, which greatly improves compile times of Clang itself,
/// as well as binary size.
///
/// Instead of functions (e.g. shouldVisitImplicitCode()), this class
/// uses member variables (e.g. ShouldVisitImplicitCode) to control
/// visitation behaviour.
///
/// However, there is no support for overriding some of the less commonly
/// used features of the RAV, such as WalkUpFromX or attribute traversal
/// (attributes can still be traversed, but you can't change what happens
/// when we traverse one).
///
/// The following is a list of RAV features that are NOT customisable:
///
///   - Visiting attributes,
///   - Overriding WalkUpFromX,
///   - Overriding getStmtChildren().
///
/// Furthermore, post-order traversal is not supported at all.
///
/// Prefer to use this over RecursiveASTVisitor unless you absolutely
/// need to use one of the features listed above (e.g. overriding
/// WalkUpFromX or post-order traversal).
///
/// \see RecursiveASTVisitor.
template <bool IsConst> class DynamicRecursiveASTVisitorBase {
protected:
  template <typename ASTNode>
  using MaybeConst = std::conditional_t<IsConst, const ASTNode, ASTNode>;

public:
  /// Whether this visitor should recurse into template instantiations.
  bool ShouldVisitTemplateInstantiations = false;

  /// Whether this visitor should recurse into the types of TypeLocs.
  bool ShouldWalkTypesOfTypeLocs = true;

  /// Whether this visitor should recurse into implicit code, e.g.
  /// implicit constructors and destructors.
  bool ShouldVisitImplicitCode = false;

  /// Whether this visitor should recurse into lambda body.
  bool ShouldVisitLambdaBody = true;

protected:
  DynamicRecursiveASTVisitorBase() = default;
  DynamicRecursiveASTVisitorBase(DynamicRecursiveASTVisitorBase &&) = default;
  DynamicRecursiveASTVisitorBase(const DynamicRecursiveASTVisitorBase &) =
      default;
  DynamicRecursiveASTVisitorBase &
  operator=(DynamicRecursiveASTVisitorBase &&) = default;
  DynamicRecursiveASTVisitorBase &
  operator=(const DynamicRecursiveASTVisitorBase &) = default;

public:
  virtual void anchor();
  virtual ~DynamicRecursiveASTVisitorBase() = default;

  /// Recursively visits an entire AST, starting from the TranslationUnitDecl.
  /// \returns false if visitation was terminated early.
  virtual bool TraverseAST(MaybeConst<ASTContext> &AST);

  /// Recursively visit an attribute, by dispatching to
  /// Traverse*Attr() based on the argument's dynamic type.
  ///
  /// \returns false if the visitation was terminated early, true
  /// otherwise (including when the argument is a Null type location).
  virtual bool TraverseAttr(MaybeConst<Attr> *At);

  /// Recursively visit a constructor initializer.  This
  /// automatically dispatches to another visitor for the initializer
  /// expression, but not for the name of the initializer, so may
  /// be overridden for clients that need access to the name.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool
  TraverseConstructorInitializer(MaybeConst<CXXCtorInitializer> *Init);

  /// Recursively visit a base specifier. This can be overridden by a
  /// subclass.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base);

  /// Recursively visit a declaration, by dispatching to
  /// Traverse*Decl() based on the argument's dynamic type.
  ///
  /// \returns false if the visitation was terminated early, true
  /// otherwise (including when the argument is NULL).
  virtual bool TraverseDecl(MaybeConst<Decl> *D);

  /// Recursively visit a name with its location information.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo);

  /// Recursively visit a lambda capture. \c Init is the expression that
  /// will be used to initialize the capture.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseLambdaCapture(MaybeConst<LambdaExpr> *LE,
                                     const LambdaCapture *C,
                                     MaybeConst<Expr> *Init);

  /// Recursively visit a C++ nested-name-specifier.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseNestedNameSpecifier(NestedNameSpecifier NNS);

  /// Recursively visit a C++ nested-name-specifier with location
  /// information.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);

  /// Recursively visit a statement or expression, by
  /// dispatching to Traverse*() based on the argument's dynamic type.
  ///
  /// \returns false if the visitation was terminated early, true
  /// otherwise (including when the argument is nullptr).
  virtual bool TraverseStmt(MaybeConst<Stmt> *S);

  /// Recursively visit a template argument and dispatch to the
  /// appropriate method for the argument type.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  // FIXME: migrate callers to TemplateArgumentLoc instead.
  virtual bool TraverseTemplateArgument(const TemplateArgument &Arg);

  /// Recursively visit a template argument location and dispatch to the
  /// appropriate method for the argument type.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc);

  /// Recursively visit a set of template arguments.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  // FIXME: take a TemplateArgumentLoc* (or TemplateArgumentListInfo) instead.
  // Not virtual for now because no-one overrides it.
  bool TraverseTemplateArguments(ArrayRef<TemplateArgument> Args);

  /// Recursively visit a template name and dispatch to the
  /// appropriate method.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseTemplateName(TemplateName Template);

  /// Recursively visit a type, by dispatching to
  /// Traverse*Type() based on the argument's getTypeClass() property.
  ///
  /// \returns false if the visitation was terminated early, true
  /// otherwise (including when the argument is a Null type).
  virtual bool TraverseType(QualType T, bool TraverseQualifier = true);

  /// Recursively visit a type with location, by dispatching to
  /// Traverse*TypeLoc() based on the argument type's getTypeClass() property.
  ///
  /// \returns false if the visitation was terminated early, true
  /// otherwise (including when the argument is a Null type location).
  virtual bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true);

  /// Recursively visit an Objective-C protocol reference with location
  /// information.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc);

  /// Traverse a concept (requirement).
  virtual bool TraverseTypeConstraint(const TypeConstraint *C);
  virtual bool TraverseConceptRequirement(MaybeConst<concepts::Requirement> *R);

  virtual bool
  TraverseConceptTypeRequirement(MaybeConst<concepts::TypeRequirement> *R);

  virtual bool
  TraverseConceptExprRequirement(MaybeConst<concepts::ExprRequirement> *R);

  virtual bool
  TraverseConceptNestedRequirement(MaybeConst<concepts::NestedRequirement> *R);

  virtual bool TraverseConceptReference(MaybeConst<ConceptReference> *CR);
  virtual bool VisitConceptReference(MaybeConst<ConceptReference> *CR) {
    return true;
  }

  /// Visit a node.
  virtual bool VisitAttr(MaybeConst<Attr> *A) { return true; }
  virtual bool VisitDecl(MaybeConst<Decl> *D) { return true; }
  virtual bool VisitStmt(MaybeConst<Stmt> *S) { return true; }
  virtual bool VisitType(MaybeConst<Type> *T) { return true; }
  virtual bool VisitTypeLoc(TypeLoc TL) { return true; }

  /// Walk up from a node.
  bool WalkUpFromDecl(MaybeConst<Decl> *D) { return VisitDecl(D); }
  bool WalkUpFromStmt(MaybeConst<Stmt> *S) { return VisitStmt(S); }
  bool WalkUpFromType(MaybeConst<Type> *T) { return VisitType(T); }
  bool WalkUpFromTypeLoc(TypeLoc TL) { return VisitTypeLoc(TL); }

  /// Invoked before visiting a statement or expression via data recursion.
  ///
  /// \returns false to skip visiting the node, true otherwise.
  virtual bool dataTraverseStmtPre(MaybeConst<Stmt> *S) { return true; }

  /// Invoked after visiting a statement or expression via data recursion.
  /// This is not invoked if the previously invoked \c dataTraverseStmtPre
  /// returned false.
  ///
  /// \returns false if the visitation was terminated early, true otherwise.
  virtual bool dataTraverseStmtPost(MaybeConst<Stmt> *S) { return true; }
  virtual bool dataTraverseNode(MaybeConst<Stmt> *S);

#define DEF_TRAVERSE_TMPL_INST(kind)                                           \
  virtual bool TraverseTemplateInstantiations(                                 \
      MaybeConst<kind##TemplateDecl> *D);
  DEF_TRAVERSE_TMPL_INST(Class)
  DEF_TRAVERSE_TMPL_INST(Var)
  DEF_TRAVERSE_TMPL_INST(Function)
#undef DEF_TRAVERSE_TMPL_INST

  // Decls.
#define ABSTRACT_DECL(DECL)
#define DECL(CLASS, BASE)                                                      \
  bool WalkUpFrom##CLASS##Decl(MaybeConst<CLASS##Decl> *D);                    \
  virtual bool Traverse##CLASS##Decl(MaybeConst<CLASS##Decl> *D);
#include "clang/AST/DeclNodes.inc"

#define DECL(CLASS, BASE)                                                      \
  virtual bool Visit##CLASS##Decl(MaybeConst<CLASS##Decl> *D) { return true; }
#include "clang/AST/DeclNodes.inc"

  // Stmts.
#define ABSTRACT_STMT(STMT)
#define STMT(CLASS, PARENT) virtual bool Traverse##CLASS(MaybeConst<CLASS> *S);
#include "clang/AST/StmtNodes.inc"

#define STMT(CLASS, PARENT)                                                    \
  bool WalkUpFrom##CLASS(MaybeConst<CLASS> *S);                                \
  virtual bool Visit##CLASS(MaybeConst<CLASS> *S) { return true; }
#include "clang/AST/StmtNodes.inc"

  // Types.
#define ABSTRACT_TYPE(CLASS, BASE)
#define TYPE(CLASS, BASE)                                                      \
  bool WalkUpFrom##CLASS##Type(MaybeConst<CLASS##Type> *T);                    \
  virtual bool Traverse##CLASS##Type(MaybeConst<CLASS##Type> *T,               \
                                     bool TraverseQualifier = true);
#include "clang/AST/TypeNodes.inc"

#define TYPE(CLASS, BASE)                                                      \
  virtual bool Visit##CLASS##Type(MaybeConst<CLASS##Type> *T) { return true; }
#include "clang/AST/TypeNodes.inc"

  // TypeLocs.
#define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE)                                                   \
  virtual bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL,                     \
                                        bool TraverseQualifier);
#include "clang/AST/TypeLocNodes.def"

#define TYPELOC(CLASS, BASE)                                                   \
  bool WalkUpFrom##CLASS##TypeLoc(CLASS##TypeLoc TL);                          \
  virtual bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }
#include "clang/AST/TypeLocNodes.def"
};

extern template class DynamicRecursiveASTVisitorBase<false>;
extern template class DynamicRecursiveASTVisitorBase<true>;

using DynamicRecursiveASTVisitor =
    DynamicRecursiveASTVisitorBase</*Const=*/false>;
using ConstDynamicRecursiveASTVisitor =
    DynamicRecursiveASTVisitorBase</*Const=*/true>;
} // namespace clang

#endif // LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H
