Skip to content

Commit

Permalink
[Clang] [NFC] Introduce DynamicRecursiveASTVisitor (#110040)
Browse files Browse the repository at this point in the history
See #105195 as well as the big comment in DynamicRecursiveASTVisitor.cpp
for more context.
  • Loading branch information
Sirraide authored Nov 5, 2024
1 parent 5f8b83e commit ff5551c
Show file tree
Hide file tree
Showing 3 changed files with 729 additions and 0 deletions.
276 changes: 276 additions & 0 deletions clang/include/clang/AST/DynamicRecursiveASTVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
//===--- 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.
class DynamicRecursiveASTVisitor {
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:
DynamicRecursiveASTVisitor() = default;
DynamicRecursiveASTVisitor(DynamicRecursiveASTVisitor &&) = default;
DynamicRecursiveASTVisitor(const DynamicRecursiveASTVisitor &) = default;
DynamicRecursiveASTVisitor &
operator=(DynamicRecursiveASTVisitor &&) = default;
DynamicRecursiveASTVisitor &
operator=(const DynamicRecursiveASTVisitor &) = default;

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

/// Recursively visits an entire AST, starting from the TranslationUnitDecl.
/// \returns false if visitation was terminated early.
virtual bool TraverseAST(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(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(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(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(LambdaExpr *LE, const LambdaCapture *C,
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(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);

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

/// 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(concepts::Requirement *R);
virtual bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R);
virtual bool TraverseConceptExprRequirement(concepts::ExprRequirement *R);
virtual bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R);
virtual bool TraverseConceptReference(ConceptReference *CR);
virtual bool VisitConceptReference(ConceptReference *CR) { return true; }

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

/// Walk up from a node.
bool WalkUpFromDecl(Decl *D) { return VisitDecl(D); }
bool WalkUpFromStmt(Stmt *S) { return VisitStmt(S); }
bool WalkUpFromType(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(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(Stmt *S) { return true; }
virtual bool dataTraverseNode(Stmt *S);

#define DEF_TRAVERSE_TMPL_INST(kind) \
virtual bool TraverseTemplateInstantiations(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) virtual bool Traverse##CLASS##Decl(CLASS##Decl *D);
#include "clang/AST/DeclNodes.inc"

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

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

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

// Types.
#define ABSTRACT_TYPE(CLASS, BASE)
#define TYPE(CLASS, BASE) virtual bool Traverse##CLASS##Type(CLASS##Type *T);
#include "clang/AST/TypeNodes.inc"

#define TYPE(CLASS, BASE) \
bool WalkUpFrom##CLASS##Type(CLASS##Type *T); \
virtual bool Visit##CLASS##Type(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);
#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"
};
} // namespace clang

#endif // LLVM_CLANG_AST_DYNAMIC_RECURSIVE_AST_VISITOR_H
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_clang_library(clangAST
DeclOpenMP.cpp
DeclPrinter.cpp
DeclTemplate.cpp
DynamicRecursiveASTVisitor.cpp
ParentMapContext.cpp
Expr.cpp
ExprClassification.cpp
Expand Down
Loading

0 comments on commit ff5551c

Please sign in to comment.