[clang] [clang] Adds a RecursiveASTEnterExitVisitor (PR #136136)

Christopher Taylor via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 17 06:18:56 PDT 2025


https://github.com/ct-clmsn created https://github.com/llvm/llvm-project/pull/136136

@Sirraide : This is my first PR to clang and llvm. The PR creates a RecursiveASTVisitor type called `RecursiveASTEnterExitVisitor` that allows users to implement Visit methods before visiting an AST node and after visiting an AST node. The additional functionality is additive in nature and extends the existing work. Users can optionally implement `VisitEnter<AST NodeType>`, `Visit<AST NodeType>`, and `VisitExit<AST NodeType>`. This functionality makes visitation method behavior explicitly defined.

>From 4b462c5608d9071432ec4819dd028b529d8a8217 Mon Sep 17 00:00:00 2001
From: "ct.clmsn" <ctaylor at tactcomplabs.com>
Date: Thu, 17 Apr 2025 09:04:47 -0400
Subject: [PATCH 1/2] inital import

Signed-off-by: ct.clmsn <ctaylor at tactcomplabs.com>
---
 .../clang/AST/RecursiveASTEnterExitVisitor.h  | 4187 +++++++++++++++++
 clang/include/clang/AST/RecursiveASTVisitor.h |    3 +
 clang/include/clang/AST/StmtOpenACC.h         |    1 +
 clang/unittests/Tooling/CMakeLists.txt        |   34 +
 ...siveASTEnterExitVisitorTestDeclVisitor.cpp |  138 +
 ...STEnterExitVisitorTestPostOrderVisitor.cpp |  114 +
 ...eASTEnterExitVisitorTestTypeLocVisitor.cpp |  100 +
 .../Attr.cpp                                  |   51 +
 .../BitfieldInitializer.cpp                   |   34 +
 .../CXXBoolLiteralExpr.cpp                    |   36 +
 .../CXXMemberCall.cpp                         |   97 +
 .../CXXMethodDecl.cpp                         |   73 +
 .../CXXOperatorCallExprTraverser.cpp          |   36 +
 .../CallbacksBinaryOperator.cpp               |  211 +
 .../CallbacksCallExpr.cpp                     |  249 +
 .../CallbacksCommon.h                         |  102 +
 .../CallbacksCompoundAssignOperator.cpp       |  212 +
 .../CallbacksLeaf.cpp                         |  285 ++
 .../CallbacksUnaryOperator.cpp                |  201 +
 .../Class.cpp                                 |   42 +
 .../Concept.cpp                               |  167 +
 .../ConstructExpr.cpp                         |   66 +
 .../DeclRefExpr.cpp                           |  116 +
 .../DeductionGuide.cpp                        |   83 +
 .../ImplicitCtor.cpp                          |   40 +
 .../ImplicitCtorInitializer.cpp               |   52 +
 .../InitListExprPostOrder.cpp                 |   36 +
 .../InitListExprPostOrderNoQueue.cpp          |   40 +
 .../InitListExprPreOrder.cpp                  |   48 +
 .../InitListExprPreOrderNoQueue.cpp           |   37 +
 .../IntegerLiteral.cpp                        |   32 +
 .../LambdaDefaultCapture.cpp                  |   34 +
 .../LambdaExpr.cpp                            |   99 +
 .../LambdaTemplateParams.cpp                  |   52 +
 .../MemberPointerTypeLoc.cpp                  |   58 +
 .../NestedNameSpecifiers.cpp                  |   73 +
 .../ParenExpr.cpp                             |   30 +
 .../TemplateArgumentLocTraverser.cpp          |   38 +
 .../TraversalScope.cpp                        |   58 +
 39 files changed, 7365 insertions(+)
 create mode 100644 clang/include/clang/AST/RecursiveASTEnterExitVisitor.h
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Attr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXOperatorCallExprTraverser.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksBinaryOperator.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCommon.h
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCompoundAssignOperator.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksUnaryOperator.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Class.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Concept.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrderNoQueue.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrderNoQueue.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ParenExpr.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TemplateArgumentLocTraverser.cpp
 create mode 100644 clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TraversalScope.cpp

diff --git a/clang/include/clang/AST/RecursiveASTEnterExitVisitor.h b/clang/include/clang/AST/RecursiveASTEnterExitVisitor.h
new file mode 100644
index 0000000000000..fd275cc75e9a5
--- /dev/null
+++ b/clang/include/clang/AST/RecursiveASTEnterExitVisitor.h
@@ -0,0 +1,4187 @@
+//===--- RecursiveASTEnterExitVisitor.h - Recursive 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 RecursiveASTEnterExitVisitor interface, which recursively
+//  traverses the entire AST.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_AST_ENTEREXITRECURSIVEASTVISITOR_H
+#define LLVM_CLANG_AST_ENTEREXITRECURSIVEASTVISITOR_H
+
+#include "clang/AST/ASTConcept.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclOpenACC.h"
+#include "clang/AST/DeclOpenMP.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/LambdaCapture.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/OpenACCClause.h"
+#include "clang/AST/OpenMPClause.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/AST/StmtOpenACC.h"
+#include "clang/AST/StmtOpenMP.h"
+#include "clang/AST/StmtSYCL.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/OpenMPKinds.h"
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <cstddef>
+#include <type_traits>
+
+namespace clang {
+
+// A helper macro to implement short-circuiting when recursing.  It
+// invokes CALL_EXPR, which must be a method call, on the derived
+// object (s.t. a user of RecursiveASTEnterExitVisitor can override the method
+// in CALL_EXPR).
+#define TRY_TO(CALL_EXPR)                                                      \
+  do {                                                                         \
+    if (!getDerived().CALL_EXPR)                                               \
+      return false;                                                            \
+  } while (false)
+
+/// A class that does preorder or postorder
+/// depth-first traversal on the entire Clang AST and visits each node.
+///
+/// This class performs three distinct tasks:
+///   1. traverse the AST (i.e. go to each node);
+///   2. at a given node, walk up the class hierarchy, starting from
+///      the node's dynamic type, until the top-most class (e.g. Stmt,
+///      Decl, or Type) is reached.
+///   3. given a (node, class) combination, where 'class' is some base
+///      class of the dynamic type of 'node', call a user-overridable
+///      function to actually visit the node.
+///
+/// These tasks are done by three groups of methods, respectively:
+///   1. TraverseDecl(Decl *x) does task #1.  It is the entry point
+///      for traversing an AST rooted at x.  This method simply
+///      dispatches (i.e. forwards) to TraverseFoo(Foo *x) where Foo
+///      is the dynamic type of *x, which calls WalkUpFromFoo(x) and
+///      then recursively visits the child nodes of x.
+///      TraverseStmt(Stmt *x) and TraverseType(QualType x) work
+///      similarly.
+///   2. WalkUpFromFoo(Foo *x) does task #2.  It does not try to visit
+///      any child node of x.  Instead, it first calls WalkUpFromBar(x)
+///      where Bar is the direct parent class of Foo (unless Foo has
+///      no parent), and then calls VisitFoo(x) (see the next list item).
+///   3. VisitFoo(Foo *x) does task #3.
+///
+/// These three method groups are tiered (Traverse* > WalkUpFrom* >
+/// Visit*).  A method (e.g. Traverse*) may call methods from the same
+/// tier (e.g. other Traverse*) or one tier lower (e.g. WalkUpFrom*).
+/// It may not call methods from a higher tier.
+///
+/// Note that since WalkUpFromFoo() calls WalkUpFromBar() (where Bar
+/// is Foo's super class) before calling VisitFoo(), the result is
+/// that the Visit*() methods for a given node are called in the
+/// top-down order (e.g. for a node of type NamespaceDecl, the order will
+/// be VisitDecl(), VisitNamedDecl(), and then VisitNamespaceDecl()).
+///
+/// This scheme guarantees that all Visit*() calls for the same AST
+/// node are grouped together.  In other words, Visit*() methods for
+/// different nodes are never interleaved.
+///
+/// Clients of this visitor should subclass the visitor (providing
+/// themselves as the template argument, using the curiously recurring
+/// template pattern) and override any of the Traverse*, WalkUpFrom*,
+/// and Visit* methods for declarations, types, statements,
+/// expressions, or other AST nodes where the visitor should customize
+/// behavior.  Most users only need to override Visit*.  Advanced
+/// users may override Traverse* and WalkUpFrom* to implement custom
+/// traversal strategies.  Returning false from one of these overridden
+/// functions will abort the entire traversal.
+///
+/// By default, this visitor tries to visit every part of the explicit
+/// source code exactly once.  The default policy towards templates
+/// is to descend into the 'pattern' class or function body, not any
+/// explicit or implicit instantiations.  Explicit specializations
+/// are still visited, and the patterns of partial specializations
+/// are visited separately.  This behavior can be changed by
+/// overriding shouldVisitTemplateInstantiations() in the derived class
+/// to return true, in which case all known implicit and explicit
+/// instantiations will be visited at the same time as the pattern
+/// from which they were produced.
+///
+/// By default, this visitor preorder traverses the AST. If postorder traversal
+/// is needed, the \c shouldTraversePostOrder method needs to be overridden
+/// to return \c true.
+template <typename Derived> class RecursiveASTEnterExitVisitor {
+public:
+  /// A queue used for performing data recursion over statements.
+  /// Parameters involving this type are used to implement data
+  /// recursion over Stmts and Exprs within this class, and should
+  /// typically not be explicitly specified by derived classes.
+  /// The bool bit indicates whether the statement has been traversed or not.
+  typedef SmallVectorImpl<llvm::PointerIntPair<Stmt *, 1, bool>>
+    DataRecursionQueue;
+
+  /// Return a reference to the derived class.
+  Derived &getDerived() { return *static_cast<Derived *>(this); }
+
+  /// Return whether this visitor should recurse into
+  /// template instantiations.
+  bool shouldVisitTemplateInstantiations() const { return false; }
+
+  /// Return whether this visitor should recurse into the types of
+  /// TypeLocs.
+  bool shouldWalkTypesOfTypeLocs() const { return true; }
+
+  /// Return whether this visitor should recurse into implicit
+  /// code, e.g., implicit constructors and destructors.
+  bool shouldVisitImplicitCode() const { return false; }
+
+  /// Return whether this visitor should recurse into lambda body
+  bool shouldVisitLambdaBody() const { return true; }
+
+  /// Return whether this visitor should traverse post-order.
+  bool shouldTraversePostOrder() const { return false; }
+
+  /// Recursively visits an entire AST, starting from the TranslationUnitDecl.
+  /// \returns false if visitation was terminated early.
+  bool TraverseAST(ASTContext &AST) {
+    // Currently just an alias for TraverseDecl(TUDecl), but kept in case
+    // we change the implementation again.
+    return getDerived().TraverseDecl(AST.getTranslationUnitDecl());
+  }
+
+  /// 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).
+  bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr);
+
+  /// Invoked before visiting a statement or expression via data recursion.
+  ///
+  /// \returns false to skip visiting the node, true otherwise.
+  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.
+  bool dataTraverseStmtPost(Stmt *S) { return true; }
+
+  /// 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).
+  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).
+  bool TraverseTypeLoc(TypeLoc TL);
+
+  /// 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).
+  bool TraverseAttr(Attr *At);
+
+  /// 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).
+  bool TraverseDecl(Decl *D);
+
+  /// Recursively visit a C++ nested-name-specifier.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS);
+
+  /// Recursively visit a C++ nested-name-specifier with location
+  /// information.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);
+
+  /// Recursively visit a name with its location information.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo);
+
+  /// Recursively visit a template name and dispatch to the
+  /// appropriate method.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseTemplateName(TemplateName Template);
+
+  /// 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.
+  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.
+  bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc);
+
+  /// Recursively visit a set of template arguments.
+  /// This can be overridden by a subclass, but it's not expected that
+  /// will be needed -- this visitor always dispatches to another.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  // FIXME: take a TemplateArgumentLoc* (or TemplateArgumentListInfo) instead.
+  bool TraverseTemplateArguments(ArrayRef<TemplateArgument> Args);
+
+  /// Recursively visit a base specifier. This can be overridden by a
+  /// subclass.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base);
+
+  /// 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.
+  bool TraverseConstructorInitializer(CXXCtorInitializer *Init);
+
+  /// 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.
+  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+                             Expr *Init);
+
+  /// Recursively visit the syntactic or semantic form of an
+  /// initialization list.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseSynOrSemInitListExpr(InitListExpr *S,
+                                    DataRecursionQueue *Queue = nullptr);
+
+  /// Recursively visit an Objective-C protocol reference with location
+  /// information.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc);
+
+  /// Recursively visit concept reference with location information.
+  ///
+  /// \returns false if the visitation was terminated early, true otherwise.
+  bool TraverseConceptReference(ConceptReference *CR);
+
+  // Visit concept reference.
+  bool VisitConceptReference(ConceptReference *CR) { return true; }
+  // ---- Methods on Attrs ----
+
+  // Visit an attribute.
+  bool VisitAttr(Attr *A) { return true; }
+
+// Declare Traverse* and empty Visit* for all Attr classes.
+#define ATTR_VISITOR_DECLS_ONLY
+#include "clang/AST/AttrVisitor.inc"
+#undef ATTR_VISITOR_DECLS_ONLY
+
+// ---- Methods on Stmts ----
+
+  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
+
+private:
+  // Traverse the given statement. If the most-derived traverse function takes a
+  // data recursion queue, pass it on; otherwise, discard it. Note that the
+  // first branch of this conditional must compile whether or not the derived
+  // class can take a queue, so if we're taking the second arm, make the first
+  // arm call our function rather than the derived class version.
+#define TRAVERSE_STMT_BASE(NAME, CLASS, VAR, QUEUE)                            \
+  (::clang::detail::has_same_member_pointer_type<                              \
+       decltype(&RecursiveASTEnterExitVisitor::Traverse##NAME),                         \
+       decltype(&Derived::Traverse##NAME)>::value                              \
+       ? static_cast<std::conditional_t<                                       \
+             ::clang::detail::has_same_member_pointer_type<                    \
+                 decltype(&RecursiveASTEnterExitVisitor::Traverse##NAME),               \
+                 decltype(&Derived::Traverse##NAME)>::value,                   \
+             Derived &, RecursiveASTEnterExitVisitor &>>(*this)                         \
+             .Traverse##NAME(static_cast<CLASS *>(VAR), QUEUE)                 \
+       : getDerived().Traverse##NAME(static_cast<CLASS *>(VAR)))
+
+// Try to traverse the given statement, or enqueue it if we're performing data
+// recursion in the middle of traversing another statement. Can only be called
+// from within a DEF_TRAVERSE_STMT body or similar context.
+#define TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S)                                     \
+  do {                                                                         \
+    if (!TRAVERSE_STMT_BASE(Stmt, Stmt, S, Queue))                             \
+      return false;                                                            \
+  } while (false)
+
+public:
+// Declare Traverse*() for all concrete Stmt classes.
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT) \
+  bool Traverse##CLASS(CLASS *S, DataRecursionQueue *Queue = nullptr);
+#include "clang/AST/StmtNodes.inc"
+  // The above header #undefs ABSTRACT_STMT and STMT upon exit.
+
+  // Define WalkUpFrom*() and empty Visit*() for all Stmt classes.
+  bool WalkUpFromStmt(Stmt *S) { return getDerived().VisitEnterStmt(S) && getDerived().VisitStmt(S) && getDerived().VisitExitStmt(S); }
+  bool VisitEnterStmt(Stmt *S) { return true; }
+  bool VisitStmt(Stmt *S) { return true; }
+  bool VisitExitStmt(Stmt *S) { return true; }
+#define STMT(CLASS, PARENT)                                                    \
+  bool WalkUpFrom##CLASS(CLASS *S) {                                           \
+    TRY_TO(WalkUpFrom##PARENT(S));                                             \
+    TRY_TO(VisitEnter##CLASS(S));                                              \
+    TRY_TO(Visit##CLASS(S));                                                   \
+    TRY_TO(VisitExit##CLASS(S));                                               \
+    return true;                                                               \
+  }                                                                            \
+  bool VisitEnter##CLASS(CLASS *S) { return true; }                            \
+  bool Visit##CLASS(CLASS *S) { return true; }                                 \
+  bool VisitExit##CLASS(CLASS *S) { return true; }
+#include "clang/AST/StmtNodes.inc"
+
+// ---- Methods on Types ----
+// FIXME: revamp to take TypeLoc's rather than Types.
+
+// Declare Traverse*() for all concrete Type classes.
+#define ABSTRACT_TYPE(CLASS, BASE)
+#define TYPE(CLASS, BASE) bool Traverse##CLASS##Type(CLASS##Type *T);
+#include "clang/AST/TypeNodes.inc"
+  // The above header #undefs ABSTRACT_TYPE and TYPE upon exit.
+
+  // Define WalkUpFrom*() and empty Visit*() for all Type classes.
+  bool WalkUpFromType(Type *T) { return getDerived().VisitEnterType(T) && getDerived().VisitType(T) && getDerived().VisitExitType(T); }
+  bool VisitEnterType(Type *T) { return true; }
+  bool VisitType(Type *T) { return true; }
+  bool VisitExitType(Type *T) { return true; }
+#define TYPE(CLASS, BASE)                                                      \
+  bool WalkUpFrom##CLASS##Type(CLASS##Type *T) {                               \
+    TRY_TO(WalkUpFrom##BASE(T));                                               \
+    TRY_TO(VisitEnter##CLASS##Type(T));                                        \
+    TRY_TO(Visit##CLASS##Type(T));                                             \
+    TRY_TO(VisitExit##CLASS##Type(T));                                         \
+    return true;                                                               \
+  }                                                                            \
+  bool VisitEnter##CLASS##Type(CLASS##Type *T) { return true; }                \
+  bool Visit##CLASS##Type(CLASS##Type *T) { return true; }                     \
+  bool VisitExit##CLASS##Type(CLASS##Type *T) { return true; }
+#include "clang/AST/TypeNodes.inc"
+
+// ---- Methods on TypeLocs ----
+// FIXME: this currently just calls the matching Type methods
+
+// Declare Traverse*() for all concrete TypeLoc classes.
+#define ABSTRACT_TYPELOC(CLASS, BASE)
+#define TYPELOC(CLASS, BASE) bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL);
+#include "clang/AST/TypeLocNodes.def"
+  // The above header #undefs ABSTRACT_TYPELOC and TYPELOC upon exit.
+
+  // Define WalkUpFrom*() and empty Visit*() for all TypeLoc classes.
+  bool WalkUpFromTypeLoc(TypeLoc TL) {
+     return getDerived().VisitEnterTypeLoc(TL) &&
+        getDerived().VisitTypeLoc(TL) &&
+        getDerived().VisitExitTypeLoc(TL);
+  }
+  bool VisitEnterTypeLoc(TypeLoc TL) { return true; }
+  bool VisitTypeLoc(TypeLoc TL) { return true; }
+  bool VisitExitTypeLoc(TypeLoc TL) { return true; }
+
+  // QualifiedTypeLoc and UnqualTypeLoc are not declared in
+  // TypeNodes.inc and thus need to be handled specially.
+  bool WalkUpFromQualifiedTypeLoc(QualifiedTypeLoc TL) {
+    return getDerived().VisitEnterUnqualTypeLoc(TL.getUnqualifiedLoc())
+       && getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc())
+       && getDerived().VisitExitUnqualTypeLoc(TL.getUnqualifiedLoc());
+  }
+  bool VisitEnterQualifiedTypeLoc(QualifiedTypeLoc TL) { return true; }
+  bool VisitQualifiedTypeLoc(QualifiedTypeLoc TL) { return true; }
+  bool VisitExitQualifiedTypeLoc(QualifiedTypeLoc TL) { return true; }
+
+  bool WalkUpFromUnqualTypeLoc(UnqualTypeLoc TL) {
+    return getDerived().VisitEnterUnqualTypeLoc(TL.getUnqualifiedLoc())
+       && getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc())
+       && getDerived().VisitExitUnqualTypeLoc(TL.getUnqualifiedLoc());
+  }
+  bool VisitEnterUnqualTypeLoc(UnqualTypeLoc TL) { return true; }
+  bool VisitUnqualTypeLoc(UnqualTypeLoc TL) { return true; }
+  bool VisitExitUnqualTypeLoc(UnqualTypeLoc TL) { return true; }
+
+// Note that BASE includes trailing 'Type' which CLASS doesn't.
+#define TYPE(CLASS, BASE)                                                      \
+  bool WalkUpFrom##CLASS##TypeLoc(CLASS##TypeLoc TL) {                         \
+    TRY_TO(WalkUpFrom##BASE##Loc(TL));                                         \
+    TRY_TO(VisitEnter##CLASS##TypeLoc(TL));                                    \
+    TRY_TO(Visit##CLASS##TypeLoc(TL));                                         \
+    TRY_TO(VisitExit##CLASS##TypeLoc(TL));                                     \
+    return true;                                                               \
+  }                                                                            \
+  bool VisitEnter##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }          \
+  bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }               \
+  bool VisitExit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }
+#include "clang/AST/TypeNodes.inc"
+
+// ---- Methods on Decls ----
+
+// Declare Traverse*() for all concrete Decl classes.
+#define ABSTRACT_DECL(DECL)
+#define DECL(CLASS, BASE) bool Traverse##CLASS##Decl(CLASS##Decl *D);
+#include "clang/AST/DeclNodes.inc"
+  // The above header #undefs ABSTRACT_DECL and DECL upon exit.
+
+  // Define WalkUpFrom*() and empty Visit*() for all Decl classes.
+  // Define WalkUpFrom*() and empty Visit*() for all Decl classes.
+  bool WalkUpFromDecl(Decl *D) {
+     return getDerived().VisitEnterDecl(D) &&
+        getDerived().VisitDecl(D) &&
+        getDerived().VisitExitDecl(D);
+  }
+  bool VisitEnterDecl(Decl *D) { return true; }
+  bool VisitDecl(Decl *D) { return true; }
+  bool VisitExitDecl(Decl *D) { return true; }
+#define DECL(CLASS, BASE)                                                      \
+  bool WalkUpFrom##CLASS##Decl(CLASS##Decl *D) {                               \
+    TRY_TO(WalkUpFrom##BASE(D));                                               \
+    TRY_TO(VisitEnter##CLASS##Decl(D));                                        \
+    TRY_TO(Visit##CLASS##Decl(D));                                             \
+    TRY_TO(VisitExit##CLASS##Decl(D));                                         \
+    return true;                                                               \
+  }                                                                            \
+  bool VisitEnter##CLASS##Decl(CLASS##Decl *D) { return true; }                \
+  bool Visit##CLASS##Decl(CLASS##Decl *D) { return true; }                     \
+  bool VisitExit##CLASS##Decl(CLASS##Decl *D) { return true; }
+#include "clang/AST/DeclNodes.inc"
+
+  bool canIgnoreChildDeclWhileTraversingDeclContext(const Decl *Child);
+
+#define DEF_TRAVERSE_TMPL_INST(TMPLDECLKIND)                                   \
+  bool TraverseTemplateInstantiations(TMPLDECLKIND##TemplateDecl *D);
+  DEF_TRAVERSE_TMPL_INST(Class)
+  DEF_TRAVERSE_TMPL_INST(Var)
+  DEF_TRAVERSE_TMPL_INST(Function)
+#undef DEF_TRAVERSE_TMPL_INST
+
+  bool TraverseTypeConstraint(const TypeConstraint *C);
+
+  bool TraverseConceptRequirement(concepts::Requirement *R);
+  bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R);
+  bool TraverseConceptExprRequirement(concepts::ExprRequirement *R);
+  bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R);
+
+  bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
+
+private:
+  // These are helper methods used by more than one Traverse* method.
+  bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
+
+  // Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
+  template <typename T>
+  bool TraverseDeclTemplateParameterLists(T *D);
+
+  bool TraverseTemplateTypeParamDeclConstraints(const TemplateTypeParmDecl *D);
+
+  bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
+                                          unsigned Count);
+  bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
+  bool TraverseRecordHelper(RecordDecl *D);
+  bool TraverseCXXRecordHelper(CXXRecordDecl *D);
+  bool TraverseDeclaratorHelper(DeclaratorDecl *D);
+  bool TraverseDeclContextHelper(DeclContext *DC);
+  bool TraverseFunctionHelper(FunctionDecl *D);
+  bool TraverseVarHelper(VarDecl *D);
+  bool TraverseOMPExecutableDirective(OMPExecutableDirective *S);
+  bool TraverseOMPLoopDirective(OMPLoopDirective *S);
+  bool TraverseOMPClause(OMPClause *C);
+#define GEN_CLANG_CLAUSE_CLASS
+#define CLAUSE_CLASS(Enum, Str, Class) bool Visit##Class(Class *C);
+#include "llvm/Frontend/OpenMP/OMP.inc"
+  /// Process clauses with list of variables.
+  template <typename T> bool VisitOMPClauseList(T *Node);
+  /// Process clauses with pre-initis.
+  bool VisitOMPClauseWithPreInit(OMPClauseWithPreInit *Node);
+  bool VisitOMPClauseWithPostUpdate(OMPClauseWithPostUpdate *Node);
+
+  bool PostVisitStmt(Stmt *S);
+  bool TraverseOpenACCConstructStmt(OpenACCConstructStmt *S);
+  bool
+  TraverseOpenACCAssociatedStmtConstruct(OpenACCAssociatedStmtConstruct *S);
+  bool VisitOpenACCClauseList(ArrayRef<const OpenACCClause *>);
+  bool VisitOpenACCClause(const OpenACCClause *);
+};
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTypeConstraint(
+    const TypeConstraint *C) {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO(TraverseConceptReference(C->getConceptReference()));
+    return true;
+  }
+  if (Expr *IDC = C->getImmediatelyDeclaredConstraint()) {
+    TRY_TO(TraverseStmt(IDC));
+  } else {
+    // Avoid traversing the ConceptReference in the TypeConstraint
+    // if we have an immediately-declared-constraint, otherwise
+    // we'll end up visiting the concept and the arguments in
+    // the TC twice.
+    TRY_TO(TraverseConceptReference(C->getConceptReference()));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConceptRequirement(
+    concepts::Requirement *R) {
+  switch (R->getKind()) {
+  case concepts::Requirement::RK_Type:
+    return getDerived().TraverseConceptTypeRequirement(
+        cast<concepts::TypeRequirement>(R));
+  case concepts::Requirement::RK_Simple:
+  case concepts::Requirement::RK_Compound:
+    return getDerived().TraverseConceptExprRequirement(
+        cast<concepts::ExprRequirement>(R));
+  case concepts::Requirement::RK_Nested:
+    return getDerived().TraverseConceptNestedRequirement(
+        cast<concepts::NestedRequirement>(R));
+  }
+  llvm_unreachable("unexpected case");
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::dataTraverseNode(Stmt *S,
+                                                    DataRecursionQueue *Queue) {
+  // Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
+  switch (S->getStmtClass()) {
+  case Stmt::NoStmtClass:
+    break;
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT)                                                    \
+  case Stmt::CLASS##Class:                                                     \
+    return TRAVERSE_STMT_BASE(CLASS, CLASS, S, Queue);
+#include "clang/AST/StmtNodes.inc"
+  }
+
+  return true;
+}
+
+#undef DISPATCH_STMT
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConceptTypeRequirement(
+    concepts::TypeRequirement *R) {
+  if (R->isSubstitutionFailure())
+    return true;
+  return getDerived().TraverseTypeLoc(R->getType()->getTypeLoc());
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConceptExprRequirement(
+    concepts::ExprRequirement *R) {
+  if (!R->isExprSubstitutionFailure())
+    TRY_TO(TraverseStmt(R->getExpr()));
+  auto &RetReq = R->getReturnTypeRequirement();
+  if (RetReq.isTypeConstraint()) {
+    if (getDerived().shouldVisitImplicitCode()) {
+      TRY_TO(TraverseTemplateParameterListHelper(
+          RetReq.getTypeConstraintTemplateParameterList()));
+    } else {
+      // Template parameter list is implicit, visit constraint directly.
+      TRY_TO(TraverseTypeConstraint(RetReq.getTypeConstraint()));
+    }
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConceptNestedRequirement(
+    concepts::NestedRequirement *R) {
+  if (!R->hasInvalidConstraint())
+    return getDerived().TraverseStmt(R->getConstraintExpr());
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::PostVisitStmt(Stmt *S) {
+  // In pre-order traversal mode, each Traverse##STMT method is responsible for
+  // calling WalkUpFrom. Therefore, if the user overrides Traverse##STMT and
+  // does not call the default implementation, the WalkUpFrom callback is not
+  // called. Post-order traversal mode should provide the same behavior
+  // regarding method overrides.
+  //
+  // In post-order traversal mode the Traverse##STMT method, when it receives a
+  // DataRecursionQueue, can't call WalkUpFrom after traversing children because
+  // it only enqueues the children and does not traverse them. TraverseStmt
+  // traverses the enqueued children, and we call WalkUpFrom here.
+  //
+  // However, to make pre-order and post-order modes identical with regards to
+  // whether they call WalkUpFrom at all, we call WalkUpFrom if and only if the
+  // user did not override the Traverse##STMT method. We implement the override
+  // check with isSameMethod calls below.
+
+  switch (S->getStmtClass()) {
+  case Stmt::NoStmtClass:
+    break;
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT)                                                    \
+  case Stmt::CLASS##Class:                                                     \
+    if (::clang::detail::isSameMethod(&RecursiveASTEnterExitVisitor::Traverse##CLASS,   \
+                                      &Derived::Traverse##CLASS)) {            \
+      TRY_TO(WalkUpFrom##CLASS(static_cast<CLASS *>(S)));                      \
+    }                                                                          \
+    break;
+#define INITLISTEXPR(CLASS, PARENT)                                            \
+  case Stmt::CLASS##Class:                                                     \
+    if (::clang::detail::isSameMethod(&RecursiveASTEnterExitVisitor::Traverse##CLASS,   \
+                                      &Derived::Traverse##CLASS)) {            \
+      auto ILE = static_cast<CLASS *>(S);                                      \
+      if (auto Syn = ILE->isSemanticForm() ? ILE->getSyntacticForm() : ILE)    \
+        TRY_TO(WalkUpFrom##CLASS(Syn));                                        \
+      if (auto Sem = ILE->isSemanticForm() ? ILE : ILE->getSemanticForm())     \
+        TRY_TO(WalkUpFrom##CLASS(Sem));                                        \
+    }                                                                          \
+    break;
+#include "clang/AST/StmtNodes.inc"
+  }
+
+  return true;
+}
+
+#undef DISPATCH_STMT
+
+// Inlining this method can lead to large code size and compile-time increases
+// without any benefit to runtime performance.
+template <typename Derived>
+LLVM_ATTRIBUTE_NOINLINE bool
+RecursiveASTEnterExitVisitor<Derived>::TraverseStmt(Stmt *S, DataRecursionQueue *Queue) {
+  if (!S)
+    return true;
+
+  if (Queue) {
+    Queue->push_back({S, false});
+    return true;
+  }
+
+  SmallVector<llvm::PointerIntPair<Stmt *, 1, bool>, 8> LocalQueue;
+  LocalQueue.push_back({S, false});
+
+  while (!LocalQueue.empty()) {
+    auto &CurrSAndVisited = LocalQueue.back();
+    Stmt *CurrS = CurrSAndVisited.getPointer();
+    bool Visited = CurrSAndVisited.getInt();
+    if (Visited) {
+      LocalQueue.pop_back();
+      TRY_TO(dataTraverseStmtPost(CurrS));
+      if (getDerived().shouldTraversePostOrder()) {
+        TRY_TO(PostVisitStmt(CurrS));
+      }
+      continue;
+    }
+
+    if (getDerived().dataTraverseStmtPre(CurrS)) {
+      CurrSAndVisited.setInt(true);
+      size_t N = LocalQueue.size();
+      TRY_TO(dataTraverseNode(CurrS, &LocalQueue));
+      // Process new children in the order they were added.
+      std::reverse(LocalQueue.begin() + N, LocalQueue.end());
+    } else {
+      LocalQueue.pop_back();
+    }
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseType(QualType T) {
+  if (T.isNull())
+    return true;
+
+  switch (T->getTypeClass()) {
+#define ABSTRACT_TYPE(CLASS, BASE)
+#define TYPE(CLASS, BASE)                                                      \
+  case Type::CLASS:                                                            \
+    return getDerived().Traverse##CLASS##Type(                                 \
+        static_cast<CLASS##Type *>(const_cast<Type *>(T.getTypePtr())));
+#include "clang/AST/TypeNodes.inc"
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTypeLoc(TypeLoc TL) {
+  if (TL.isNull())
+    return true;
+
+  switch (TL.getTypeLocClass()) {
+#define ABSTRACT_TYPELOC(CLASS, BASE)
+#define TYPELOC(CLASS, BASE)                                                   \
+  case TypeLoc::CLASS:                                                         \
+    return getDerived().Traverse##CLASS##TypeLoc(TL.castAs<CLASS##TypeLoc>());
+#include "clang/AST/TypeLocNodes.def"
+  }
+
+  return true;
+}
+
+// Define the Traverse*Attr(Attr* A) methods
+#define VISITORCLASS RecursiveASTEnterExitVisitor
+#include "clang/AST/AttrVisitor.inc"
+#undef VISITORCLASS
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseDecl(Decl *D) {
+  if (!D)
+    return true;
+
+  // As a syntax visitor, by default we want to ignore declarations for
+  // implicit declarations (ones not typed explicitly by the user).
+  if (!getDerived().shouldVisitImplicitCode()) {
+    if (D->isImplicit()) {
+      // For an implicit template type parameter, its type constraints are not
+      // implicit and are not represented anywhere else. We still need to visit
+      // them.
+      if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
+        return TraverseTemplateTypeParamDeclConstraints(TTPD);
+      return true;
+    }
+
+    // Deduction guides for alias templates are always synthesized, so they
+    // should not be traversed unless shouldVisitImplicitCode() returns true.
+    //
+    // It's important to note that checking the implicit bit is not efficient
+    // for the alias case. For deduction guides synthesized from explicit
+    // user-defined deduction guides, we must maintain the explicit bit to
+    // ensure correct overload resolution.
+    if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+      if (llvm::isa_and_present<TypeAliasTemplateDecl>(
+              FTD->getDeclName().getCXXDeductionGuideTemplate()))
+        return true;
+  }
+
+  switch (D->getKind()) {
+#define ABSTRACT_DECL(DECL)
+#define DECL(CLASS, BASE)                                                      \
+  case Decl::CLASS:                                                            \
+    if (!getDerived().Traverse##CLASS##Decl(static_cast<CLASS##Decl *>(D)))    \
+      return false;                                                            \
+    break;
+#include "clang/AST/DeclNodes.inc"
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseNestedNameSpecifier(
+    NestedNameSpecifier *NNS) {
+  if (!NNS)
+    return true;
+
+  if (NNS->getPrefix())
+    TRY_TO(TraverseNestedNameSpecifier(NNS->getPrefix()));
+
+  switch (NNS->getKind()) {
+  case NestedNameSpecifier::Identifier:
+  case NestedNameSpecifier::Namespace:
+  case NestedNameSpecifier::NamespaceAlias:
+  case NestedNameSpecifier::Global:
+  case NestedNameSpecifier::Super:
+    return true;
+
+  case NestedNameSpecifier::TypeSpec:
+    TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseNestedNameSpecifierLoc(
+    NestedNameSpecifierLoc NNS) {
+  if (!NNS)
+    return true;
+
+  if (NestedNameSpecifierLoc Prefix = NNS.getPrefix())
+    TRY_TO(TraverseNestedNameSpecifierLoc(Prefix));
+
+  switch (NNS.getNestedNameSpecifier()->getKind()) {
+  case NestedNameSpecifier::Identifier:
+  case NestedNameSpecifier::Namespace:
+  case NestedNameSpecifier::NamespaceAlias:
+  case NestedNameSpecifier::Global:
+  case NestedNameSpecifier::Super:
+    return true;
+
+  case NestedNameSpecifier::TypeSpec:
+    TRY_TO(TraverseTypeLoc(NNS.getTypeLoc()));
+    break;
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseDeclarationNameInfo(
+    DeclarationNameInfo NameInfo) {
+  switch (NameInfo.getName().getNameKind()) {
+  case DeclarationName::CXXConstructorName:
+  case DeclarationName::CXXDestructorName:
+  case DeclarationName::CXXConversionFunctionName:
+    if (TypeSourceInfo *TSInfo = NameInfo.getNamedTypeInfo())
+      TRY_TO(TraverseTypeLoc(TSInfo->getTypeLoc()));
+    break;
+
+  case DeclarationName::CXXDeductionGuideName:
+    TRY_TO(TraverseTemplateName(
+        TemplateName(NameInfo.getName().getCXXDeductionGuideTemplate())));
+    break;
+
+  case DeclarationName::Identifier:
+  case DeclarationName::ObjCZeroArgSelector:
+  case DeclarationName::ObjCOneArgSelector:
+  case DeclarationName::ObjCMultiArgSelector:
+  case DeclarationName::CXXOperatorName:
+  case DeclarationName::CXXLiteralOperatorName:
+  case DeclarationName::CXXUsingDirective:
+    break;
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateName(TemplateName Template) {
+  if (DependentTemplateName *DTN = Template.getAsDependentTemplateName()) {
+    TRY_TO(TraverseNestedNameSpecifier(DTN->getQualifier()));
+  } else if (QualifiedTemplateName *QTN =
+                 Template.getAsQualifiedTemplateName()) {
+    if (QTN->getQualifier()) {
+      TRY_TO(TraverseNestedNameSpecifier(QTN->getQualifier()));
+    }
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateArgument(
+    const TemplateArgument &Arg) {
+  switch (Arg.getKind()) {
+  case TemplateArgument::Null:
+  case TemplateArgument::Declaration:
+  case TemplateArgument::Integral:
+  case TemplateArgument::NullPtr:
+  case TemplateArgument::StructuralValue:
+    return true;
+
+  case TemplateArgument::Type:
+    return getDerived().TraverseType(Arg.getAsType());
+
+  case TemplateArgument::Template:
+  case TemplateArgument::TemplateExpansion:
+    return getDerived().TraverseTemplateName(
+        Arg.getAsTemplateOrTemplatePattern());
+
+  case TemplateArgument::Expression:
+    return getDerived().TraverseStmt(Arg.getAsExpr());
+
+  case TemplateArgument::Pack:
+    return getDerived().TraverseTemplateArguments(Arg.pack_elements());
+  }
+
+  return true;
+}
+
+// FIXME: no template name location?
+// FIXME: no source locations for a template argument pack?
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateArgumentLoc(
+    const TemplateArgumentLoc &ArgLoc) {
+  const TemplateArgument &Arg = ArgLoc.getArgument();
+
+  switch (Arg.getKind()) {
+  case TemplateArgument::Null:
+  case TemplateArgument::Declaration:
+  case TemplateArgument::Integral:
+  case TemplateArgument::NullPtr:
+  case TemplateArgument::StructuralValue:
+    return true;
+
+  case TemplateArgument::Type: {
+    // FIXME: how can TSI ever be NULL?
+    if (TypeSourceInfo *TSI = ArgLoc.getTypeSourceInfo())
+      return getDerived().TraverseTypeLoc(TSI->getTypeLoc());
+    else
+      return getDerived().TraverseType(Arg.getAsType());
+  }
+
+  case TemplateArgument::Template:
+  case TemplateArgument::TemplateExpansion:
+    if (ArgLoc.getTemplateQualifierLoc())
+      TRY_TO(getDerived().TraverseNestedNameSpecifierLoc(
+          ArgLoc.getTemplateQualifierLoc()));
+    return getDerived().TraverseTemplateName(
+        Arg.getAsTemplateOrTemplatePattern());
+
+  case TemplateArgument::Expression:
+    return getDerived().TraverseStmt(ArgLoc.getSourceExpression());
+
+  case TemplateArgument::Pack:
+    return getDerived().TraverseTemplateArguments(Arg.pack_elements());
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateArguments(
+    ArrayRef<TemplateArgument> Args) {
+  for (const TemplateArgument &Arg : Args)
+    TRY_TO(TraverseTemplateArgument(Arg));
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConstructorInitializer(
+    CXXCtorInitializer *Init) {
+  if (TypeSourceInfo *TInfo = Init->getTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+
+  if (Init->isWritten() || getDerived().shouldVisitImplicitCode())
+    TRY_TO(TraverseStmt(Init->getInit()));
+
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::TraverseLambdaCapture(LambdaExpr *LE,
+                                                    const LambdaCapture *C,
+                                                    Expr *Init) {
+  if (LE->isInitCapture(C))
+    TRY_TO(TraverseDecl(C->getCapturedVar()));
+  else
+    TRY_TO(TraverseStmt(Init));
+  return true;
+}
+
+// ----------------- Type traversal -----------------
+
+// This macro makes available a variable T, the passed-in type.
+#define DEF_TRAVERSE_TYPE(TYPE, CODE)                                          \
+  template <typename Derived>                                                  \
+  bool RecursiveASTEnterExitVisitor<Derived>::Traverse##TYPE(TYPE *T) {                 \
+    if (!getDerived().shouldTraversePostOrder())                               \
+      TRY_TO(WalkUpFrom##TYPE(T));                                             \
+    { CODE; }                                                                  \
+    if (getDerived().shouldTraversePostOrder())                                \
+      TRY_TO(WalkUpFrom##TYPE(T));                                             \
+    return true;                                                               \
+  }
+
+DEF_TRAVERSE_TYPE(BuiltinType, {})
+
+DEF_TRAVERSE_TYPE(ComplexType, { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(PointerType, { TRY_TO(TraverseType(T->getPointeeType())); })
+
+DEF_TRAVERSE_TYPE(BlockPointerType,
+                  { TRY_TO(TraverseType(T->getPointeeType())); })
+
+DEF_TRAVERSE_TYPE(LValueReferenceType,
+                  { TRY_TO(TraverseType(T->getPointeeType())); })
+
+DEF_TRAVERSE_TYPE(RValueReferenceType,
+                  { TRY_TO(TraverseType(T->getPointeeType())); })
+
+DEF_TRAVERSE_TYPE(MemberPointerType, {
+  TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+  if (T->isSugared())
+    TRY_TO(TraverseType(
+        QualType(T->getMostRecentCXXRecordDecl()->getTypeForDecl(), 0)));
+  TRY_TO(TraverseType(T->getPointeeType()));
+})
+
+DEF_TRAVERSE_TYPE(AdjustedType, { TRY_TO(TraverseType(T->getOriginalType())); })
+
+DEF_TRAVERSE_TYPE(DecayedType, { TRY_TO(TraverseType(T->getOriginalType())); })
+
+DEF_TRAVERSE_TYPE(ConstantArrayType, {
+  TRY_TO(TraverseType(T->getElementType()));
+  if (T->getSizeExpr())
+    TRY_TO(TraverseStmt(const_cast<Expr*>(T->getSizeExpr())));
+})
+
+DEF_TRAVERSE_TYPE(ArrayParameterType, {
+  TRY_TO(TraverseType(T->getElementType()));
+  if (T->getSizeExpr())
+    TRY_TO(TraverseStmt(const_cast<Expr *>(T->getSizeExpr())));
+})
+
+DEF_TRAVERSE_TYPE(IncompleteArrayType,
+                  { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(VariableArrayType, {
+  TRY_TO(TraverseType(T->getElementType()));
+  TRY_TO(TraverseStmt(T->getSizeExpr()));
+})
+
+DEF_TRAVERSE_TYPE(DependentSizedArrayType, {
+  TRY_TO(TraverseType(T->getElementType()));
+  if (T->getSizeExpr())
+    TRY_TO(TraverseStmt(T->getSizeExpr()));
+})
+
+DEF_TRAVERSE_TYPE(DependentAddressSpaceType, {
+  TRY_TO(TraverseStmt(T->getAddrSpaceExpr()));
+  TRY_TO(TraverseType(T->getPointeeType()));
+})
+
+DEF_TRAVERSE_TYPE(DependentVectorType, {
+  if (T->getSizeExpr())
+    TRY_TO(TraverseStmt(T->getSizeExpr()));
+  TRY_TO(TraverseType(T->getElementType()));
+})
+
+DEF_TRAVERSE_TYPE(DependentSizedExtVectorType, {
+  if (T->getSizeExpr())
+    TRY_TO(TraverseStmt(T->getSizeExpr()));
+  TRY_TO(TraverseType(T->getElementType()));
+})
+
+DEF_TRAVERSE_TYPE(VectorType, { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(ExtVectorType, { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(ConstantMatrixType,
+                  { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(DependentSizedMatrixType, {
+  if (T->getRowExpr())
+    TRY_TO(TraverseStmt(T->getRowExpr()));
+  if (T->getColumnExpr())
+    TRY_TO(TraverseStmt(T->getColumnExpr()));
+  TRY_TO(TraverseType(T->getElementType()));
+})
+
+DEF_TRAVERSE_TYPE(FunctionNoProtoType,
+                  { TRY_TO(TraverseType(T->getReturnType())); })
+
+DEF_TRAVERSE_TYPE(FunctionProtoType, {
+  TRY_TO(TraverseType(T->getReturnType()));
+
+  for (const auto &A : T->param_types()) {
+    TRY_TO(TraverseType(A));
+  }
+
+  for (const auto &E : T->exceptions()) {
+    TRY_TO(TraverseType(E));
+  }
+
+  if (Expr *NE = T->getNoexceptExpr())
+    TRY_TO(TraverseStmt(NE));
+})
+
+DEF_TRAVERSE_TYPE(UsingType, {})
+DEF_TRAVERSE_TYPE(UnresolvedUsingType, {})
+DEF_TRAVERSE_TYPE(TypedefType, {})
+
+DEF_TRAVERSE_TYPE(TypeOfExprType,
+                  { TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
+
+DEF_TRAVERSE_TYPE(TypeOfType, { TRY_TO(TraverseType(T->getUnmodifiedType())); })
+
+DEF_TRAVERSE_TYPE(DecltypeType,
+                  { TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
+
+DEF_TRAVERSE_TYPE(PackIndexingType, {
+  TRY_TO(TraverseType(T->getPattern()));
+  TRY_TO(TraverseStmt(T->getIndexExpr()));
+})
+
+DEF_TRAVERSE_TYPE(UnaryTransformType, {
+  TRY_TO(TraverseType(T->getBaseType()));
+  TRY_TO(TraverseType(T->getUnderlyingType()));
+})
+
+DEF_TRAVERSE_TYPE(AutoType, {
+  TRY_TO(TraverseType(T->getDeducedType()));
+  if (T->isConstrained()) {
+    TRY_TO(TraverseTemplateArguments(T->getTypeConstraintArguments()));
+  }
+})
+DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, {
+  TRY_TO(TraverseTemplateName(T->getTemplateName()));
+  TRY_TO(TraverseType(T->getDeducedType()));
+})
+
+DEF_TRAVERSE_TYPE(RecordType, {})
+DEF_TRAVERSE_TYPE(EnumType, {})
+DEF_TRAVERSE_TYPE(TemplateTypeParmType, {})
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {
+  TRY_TO(TraverseType(T->getReplacementType()));
+})
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {
+  TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
+})
+
+DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
+  TRY_TO(TraverseTemplateName(T->getTemplateName()));
+  TRY_TO(TraverseTemplateArguments(T->template_arguments()));
+})
+
+DEF_TRAVERSE_TYPE(InjectedClassNameType, {})
+
+DEF_TRAVERSE_TYPE(AttributedType,
+                  { TRY_TO(TraverseType(T->getModifiedType())); })
+
+DEF_TRAVERSE_TYPE(CountAttributedType, {
+  if (T->getCountExpr())
+    TRY_TO(TraverseStmt(T->getCountExpr()));
+  TRY_TO(TraverseType(T->desugar()));
+})
+
+DEF_TRAVERSE_TYPE(BTFTagAttributedType,
+                  { TRY_TO(TraverseType(T->getWrappedType())); })
+
+DEF_TRAVERSE_TYPE(HLSLAttributedResourceType,
+                  { TRY_TO(TraverseType(T->getWrappedType())); })
+
+DEF_TRAVERSE_TYPE(ParenType, { TRY_TO(TraverseType(T->getInnerType())); })
+
+DEF_TRAVERSE_TYPE(MacroQualifiedType,
+                  { TRY_TO(TraverseType(T->getUnderlyingType())); })
+
+DEF_TRAVERSE_TYPE(ElaboratedType, {
+  if (T->getQualifier()) {
+    TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+  }
+  TRY_TO(TraverseType(T->getNamedType()));
+})
+
+DEF_TRAVERSE_TYPE(DependentNameType,
+                  { TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); })
+
+DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
+  const DependentTemplateStorage &S = T->getDependentTemplateName();
+  TRY_TO(TraverseNestedNameSpecifier(S.getQualifier()));
+  TRY_TO(TraverseTemplateArguments(T->template_arguments()));
+})
+
+DEF_TRAVERSE_TYPE(PackExpansionType, { TRY_TO(TraverseType(T->getPattern())); })
+
+DEF_TRAVERSE_TYPE(ObjCTypeParamType, {})
+
+DEF_TRAVERSE_TYPE(ObjCInterfaceType, {})
+
+DEF_TRAVERSE_TYPE(ObjCObjectType, {
+  // We have to watch out here because an ObjCInterfaceType's base
+  // type is itself.
+  if (T->getBaseType().getTypePtr() != T)
+    TRY_TO(TraverseType(T->getBaseType()));
+  for (auto typeArg : T->getTypeArgsAsWritten()) {
+    TRY_TO(TraverseType(typeArg));
+  }
+})
+
+DEF_TRAVERSE_TYPE(ObjCObjectPointerType,
+                  { TRY_TO(TraverseType(T->getPointeeType())); })
+
+DEF_TRAVERSE_TYPE(AtomicType, { TRY_TO(TraverseType(T->getValueType())); })
+
+DEF_TRAVERSE_TYPE(PipeType, { TRY_TO(TraverseType(T->getElementType())); })
+
+DEF_TRAVERSE_TYPE(BitIntType, {})
+DEF_TRAVERSE_TYPE(DependentBitIntType,
+                  { TRY_TO(TraverseStmt(T->getNumBitsExpr())); })
+
+#undef DEF_TRAVERSE_TYPE
+
+// ----------------- TypeLoc traversal -----------------
+
+// This macro makes available a variable TL, the passed-in TypeLoc.
+// If requested, it calls WalkUpFrom* for the Type in the given TypeLoc,
+// in addition to WalkUpFrom* for the TypeLoc itself, such that existing
+// clients that override the WalkUpFrom*Type() and/or Visit*Type() methods
+// continue to work.
+#define DEF_TRAVERSE_TYPELOC(TYPE, CODE)                                       \
+  template <typename Derived>                                                  \
+  bool RecursiveASTEnterExitVisitor<Derived>::Traverse##TYPE##Loc(TYPE##Loc TL) {       \
+    if (!getDerived().shouldTraversePostOrder()) {                             \
+      TRY_TO(WalkUpFrom##TYPE##Loc(TL));                                       \
+      if (getDerived().shouldWalkTypesOfTypeLocs())                            \
+        TRY_TO(WalkUpFrom##TYPE(const_cast<TYPE *>(TL.getTypePtr())));         \
+    }                                                                          \
+    { CODE; }                                                                  \
+    if (getDerived().shouldTraversePostOrder()) {                              \
+      TRY_TO(WalkUpFrom##TYPE##Loc(TL));                                       \
+      if (getDerived().shouldWalkTypesOfTypeLocs())                            \
+        TRY_TO(WalkUpFrom##TYPE(const_cast<TYPE *>(TL.getTypePtr())));         \
+    }                                                                          \
+    return true;                                                               \
+  }
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) {
+  // Move this over to the 'main' typeloc tree.  Note that this is a
+  // move -- we pretend that we were really looking at the unqualified
+  // typeloc all along -- rather than a recursion, so we don't follow
+  // the normal CRTP plan of going through
+  // getDerived().TraverseTypeLoc.  If we did, we'd be traversing
+  // twice for the same type (once as a QualifiedTypeLoc version of
+  // the type, once as an UnqualifiedTypeLoc version of the type),
+  // which in effect means we'd call VisitTypeLoc twice with the
+  // 'same' type.  This solves that problem, at the cost of never
+  // seeing the qualified version of the type (unless the client
+  // subclasses TraverseQualifiedTypeLoc themselves).  It's not a
+  // perfect solution.  A perfect solution probably requires making
+  // QualifiedTypeLoc a wrapper around TypeLoc -- like QualType is a
+  // wrapper around Type* -- rather than being its own class in the
+  // type hierarchy.
+  return TraverseTypeLoc(TL.getUnqualifiedLoc());
+}
+
+DEF_TRAVERSE_TYPELOC(BuiltinType, {})
+
+// FIXME: ComplexTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(ComplexType, {
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+DEF_TRAVERSE_TYPELOC(PointerType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); })
+
+DEF_TRAVERSE_TYPELOC(BlockPointerType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); })
+
+DEF_TRAVERSE_TYPELOC(LValueReferenceType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); })
+
+DEF_TRAVERSE_TYPELOC(RValueReferenceType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); })
+
+// We traverse this in the type case as well, but how is it not reached through
+// the pointee type?
+DEF_TRAVERSE_TYPELOC(MemberPointerType, {
+  if (NestedNameSpecifierLoc QL = TL.getQualifierLoc())
+    TRY_TO(TraverseNestedNameSpecifierLoc(QL));
+//  if (auto *TSI = TL.getClassTInfo())
+//    TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+  else
+    TRY_TO(TraverseNestedNameSpecifier(TL.getTypePtr()->getQualifier()));
+//    TRY_TO(TraverseType(QualType(TL.getTypePtr()->getClass(), 0)));
+  TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+})
+
+DEF_TRAVERSE_TYPELOC(AdjustedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); })
+
+DEF_TRAVERSE_TYPELOC(DecayedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); })
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseArrayTypeLocHelper(ArrayTypeLoc TL) {
+  // This isn't available for ArrayType, but is for the ArrayTypeLoc.
+  TRY_TO(TraverseStmt(TL.getSizeExpr()));
+  return true;
+}
+
+DEF_TRAVERSE_TYPELOC(ConstantArrayType, {
+  TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+  TRY_TO(TraverseArrayTypeLocHelper(TL));
+})
+
+DEF_TRAVERSE_TYPELOC(ArrayParameterType, {
+  TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+  TRY_TO(TraverseArrayTypeLocHelper(TL));
+})
+
+DEF_TRAVERSE_TYPELOC(IncompleteArrayType, {
+  TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+  TRY_TO(TraverseArrayTypeLocHelper(TL));
+})
+
+DEF_TRAVERSE_TYPELOC(VariableArrayType, {
+  TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+  TRY_TO(TraverseArrayTypeLocHelper(TL));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentSizedArrayType, {
+  TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+  TRY_TO(TraverseArrayTypeLocHelper(TL));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentAddressSpaceType, {
+  TRY_TO(TraverseStmt(TL.getTypePtr()->getAddrSpaceExpr()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getPointeeType()));
+})
+
+// FIXME: order? why not size expr first?
+// FIXME: base VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(DependentSizedExtVectorType, {
+  if (TL.getTypePtr()->getSizeExpr())
+    TRY_TO(TraverseStmt(TL.getTypePtr()->getSizeExpr()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+// FIXME: VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(VectorType, {
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentVectorType, {
+  if (TL.getTypePtr()->getSizeExpr())
+    TRY_TO(TraverseStmt(TL.getTypePtr()->getSizeExpr()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+// FIXME: size and attributes
+// FIXME: base VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(ExtVectorType, {
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+DEF_TRAVERSE_TYPELOC(ConstantMatrixType, {
+  TRY_TO(TraverseStmt(TL.getAttrRowOperand()));
+  TRY_TO(TraverseStmt(TL.getAttrColumnOperand()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentSizedMatrixType, {
+  TRY_TO(TraverseStmt(TL.getAttrRowOperand()));
+  TRY_TO(TraverseStmt(TL.getAttrColumnOperand()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+})
+
+DEF_TRAVERSE_TYPELOC(FunctionNoProtoType,
+                     { TRY_TO(TraverseTypeLoc(TL.getReturnLoc())); })
+
+// FIXME: location of exception specifications (attributes?)
+DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
+  TRY_TO(TraverseTypeLoc(TL.getReturnLoc()));
+
+  const FunctionProtoType *T = TL.getTypePtr();
+
+  for (unsigned I = 0, E = TL.getNumParams(); I != E; ++I) {
+    if (TL.getParam(I)) {
+      TRY_TO(TraverseDecl(TL.getParam(I)));
+    } else if (I < T->getNumParams()) {
+      TRY_TO(TraverseType(T->getParamType(I)));
+    }
+  }
+
+  for (const auto &E : T->exceptions()) {
+    TRY_TO(TraverseType(E));
+  }
+
+  if (Expr *NE = T->getNoexceptExpr())
+    TRY_TO(TraverseStmt(NE));
+})
+
+DEF_TRAVERSE_TYPELOC(UsingType, {})
+DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {})
+DEF_TRAVERSE_TYPELOC(TypedefType, {})
+
+DEF_TRAVERSE_TYPELOC(TypeOfExprType,
+                     { TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
+
+DEF_TRAVERSE_TYPELOC(TypeOfType, {
+  TRY_TO(TraverseTypeLoc(TL.getUnmodifiedTInfo()->getTypeLoc()));
+})
+
+// FIXME: location of underlying expr
+DEF_TRAVERSE_TYPELOC(DecltypeType, {
+  TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr()));
+})
+
+DEF_TRAVERSE_TYPELOC(PackIndexingType, {
+  TRY_TO(TraverseType(TL.getPattern()));
+  TRY_TO(TraverseStmt(TL.getTypePtr()->getIndexExpr()));
+})
+
+DEF_TRAVERSE_TYPELOC(UnaryTransformType, {
+  TRY_TO(TraverseTypeLoc(TL.getUnderlyingTInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_TYPELOC(AutoType, {
+  TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
+  if (TL.isConstrained()) {
+    TRY_TO(TraverseConceptReference(TL.getConceptReference()));
+  }
+})
+
+DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, {
+  TRY_TO(TraverseTemplateName(TL.getTypePtr()->getTemplateName()));
+  TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
+})
+
+DEF_TRAVERSE_TYPELOC(RecordType, {})
+DEF_TRAVERSE_TYPELOC(EnumType, {})
+DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {})
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, {
+  TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType()));
+})
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, {
+  TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack()));
+})
+
+// FIXME: use the loc for the template name?
+DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
+  TRY_TO(TraverseTemplateName(TL.getTypePtr()->getTemplateName()));
+  for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
+    TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
+  }
+})
+
+DEF_TRAVERSE_TYPELOC(InjectedClassNameType, {})
+
+DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
+DEF_TRAVERSE_TYPELOC(MacroQualifiedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
+DEF_TRAVERSE_TYPELOC(AttributedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getModifiedLoc())); })
+
+DEF_TRAVERSE_TYPELOC(CountAttributedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
+DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
+
+DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType,
+                     { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
+
+DEF_TRAVERSE_TYPELOC(ElaboratedType, {
+  if (TL.getQualifierLoc()) {
+    TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+  }
+  TRY_TO(TraverseTypeLoc(TL.getNamedTypeLoc()));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentNameType, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+})
+
+DEF_TRAVERSE_TYPELOC(DependentTemplateSpecializationType, {
+  if (TL.getQualifierLoc()) {
+    TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+  }
+
+  for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
+    TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
+  }
+})
+
+DEF_TRAVERSE_TYPELOC(PackExpansionType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPatternLoc())); })
+
+DEF_TRAVERSE_TYPELOC(ObjCTypeParamType, {
+  for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) {
+    ObjCProtocolLoc ProtocolLoc(TL.getProtocol(I), TL.getProtocolLoc(I));
+    TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+  }
+})
+
+DEF_TRAVERSE_TYPELOC(ObjCInterfaceType, {})
+
+DEF_TRAVERSE_TYPELOC(ObjCObjectType, {
+  // We have to watch out here because an ObjCInterfaceType's base
+  // type is itself.
+  if (TL.getTypePtr()->getBaseType().getTypePtr() != TL.getTypePtr())
+    TRY_TO(TraverseTypeLoc(TL.getBaseLoc()));
+  for (unsigned i = 0, n = TL.getNumTypeArgs(); i != n; ++i)
+    TRY_TO(TraverseTypeLoc(TL.getTypeArgTInfo(i)->getTypeLoc()));
+  for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) {
+    ObjCProtocolLoc ProtocolLoc(TL.getProtocol(I), TL.getProtocolLoc(I));
+    TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+  }
+})
+
+DEF_TRAVERSE_TYPELOC(ObjCObjectPointerType,
+                     { TRY_TO(TraverseTypeLoc(TL.getPointeeLoc())); })
+
+DEF_TRAVERSE_TYPELOC(AtomicType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); })
+
+DEF_TRAVERSE_TYPELOC(PipeType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); })
+
+DEF_TRAVERSE_TYPELOC(BitIntType, {})
+DEF_TRAVERSE_TYPELOC(DependentBitIntType, {
+  TRY_TO(TraverseStmt(TL.getTypePtr()->getNumBitsExpr()));
+})
+
+#undef DEF_TRAVERSE_TYPELOC
+
+// ----------------- Decl traversal -----------------
+//
+// For a Decl, we automate (in the DEF_TRAVERSE_DECL macro) traversing
+// the children that come from the DeclContext associated with it.
+// Therefore each Traverse* only needs to worry about children other
+// than those.
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::canIgnoreChildDeclWhileTraversingDeclContext(
+    const Decl *Child) {
+  // BlockDecls are traversed through BlockExprs,
+  // CapturedDecls are traversed through CapturedStmts.
+  if (isa<BlockDecl>(Child) || isa<CapturedDecl>(Child))
+    return true;
+  // Lambda classes are traversed through LambdaExprs.
+  if (const CXXRecordDecl* Cls = dyn_cast<CXXRecordDecl>(Child))
+    return Cls->isLambda();
+  return false;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseDeclContextHelper(DeclContext *DC) {
+  if (!DC)
+    return true;
+
+  for (auto *Child : DC->decls()) {
+    if (!canIgnoreChildDeclWhileTraversingDeclContext(Child))
+      TRY_TO(TraverseDecl(Child));
+  }
+
+  return true;
+}
+
+// This macro makes available a variable D, the passed-in decl.
+#define DEF_TRAVERSE_DECL(DECL, CODE)                                          \
+  template <typename Derived>                                                  \
+  bool RecursiveASTEnterExitVisitor<Derived>::Traverse##DECL(DECL *D) {                 \
+    bool ShouldVisitChildren = true;                                           \
+    bool ReturnValue = true;                                                   \
+    if (!getDerived().shouldTraversePostOrder())                               \
+      TRY_TO(WalkUpFrom##DECL(D));                                             \
+    { CODE; }                                                                  \
+    if (ReturnValue && ShouldVisitChildren)                                    \
+      TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D)));             \
+    if (ReturnValue) {                                                         \
+      /* Visit any attributes attached to this declaration. */                 \
+      for (auto *I : D->attrs())                                               \
+        TRY_TO(getDerived().TraverseAttr(I));                                  \
+    }                                                                          \
+    if (ReturnValue && getDerived().shouldTraversePostOrder())                 \
+      TRY_TO(WalkUpFrom##DECL(D));                                             \
+    return ReturnValue;                                                        \
+  }
+
+DEF_TRAVERSE_DECL(AccessSpecDecl, {})
+
+DEF_TRAVERSE_DECL(BlockDecl, {
+  if (TypeSourceInfo *TInfo = D->getSignatureAsWritten())
+    TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+  TRY_TO(TraverseStmt(D->getBody()));
+  for (const auto &I : D->captures()) {
+    if (I.hasCopyExpr()) {
+      TRY_TO(TraverseStmt(I.getCopyExpr()));
+    }
+  }
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(OutlinedFunctionDecl, {
+  TRY_TO(TraverseStmt(D->getBody()));
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(CapturedDecl, {
+  TRY_TO(TraverseStmt(D->getBody()));
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(EmptyDecl, {})
+
+DEF_TRAVERSE_DECL(HLSLBufferDecl, {})
+
+DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, {
+  TRY_TO(TraverseStmt(D->getTemporaryExpr()));
+})
+
+DEF_TRAVERSE_DECL(FileScopeAsmDecl,
+                  { TRY_TO(TraverseStmt(D->getAsmString())); })
+
+DEF_TRAVERSE_DECL(TopLevelStmtDecl, { TRY_TO(TraverseStmt(D->getStmt())); })
+
+DEF_TRAVERSE_DECL(ImportDecl, {})
+
+DEF_TRAVERSE_DECL(FriendDecl, {
+  // Friend is either decl or a type.
+  if (D->getFriendType()) {
+    TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+    // Traverse any CXXRecordDecl owned by this type, since
+    // it will not be in the parent context:
+    if (auto *ET = D->getFriendType()->getType()->getAs<ElaboratedType>())
+      TRY_TO(TraverseDecl(ET->getOwnedTagDecl()));
+  } else {
+    TRY_TO(TraverseDecl(D->getFriendDecl()));
+  }
+})
+
+DEF_TRAVERSE_DECL(FriendTemplateDecl, {
+  if (D->getFriendType())
+    TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+  else
+    TRY_TO(TraverseDecl(D->getFriendDecl()));
+  for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
+    TemplateParameterList *TPL = D->getTemplateParameterList(I);
+    for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
+         ITPL != ETPL; ++ITPL) {
+      TRY_TO(TraverseDecl(*ITPL));
+    }
+  }
+})
+
+DEF_TRAVERSE_DECL(LinkageSpecDecl, {})
+
+DEF_TRAVERSE_DECL(ExportDecl, {})
+
+DEF_TRAVERSE_DECL(ObjCPropertyImplDecl, {// FIXME: implement this
+                                        })
+
+DEF_TRAVERSE_DECL(StaticAssertDecl, {
+  TRY_TO(TraverseStmt(D->getAssertExpr()));
+  TRY_TO(TraverseStmt(D->getMessage()));
+})
+
+DEF_TRAVERSE_DECL(TranslationUnitDecl, {
+  // Code in an unnamed namespace shows up automatically in
+  // decls_begin()/decls_end().  Thus we don't need to recurse on
+  // D->getAnonymousNamespace().
+
+  // If the traversal scope is set, then consider them to be the children of
+  // the TUDecl, rather than traversing (and loading?) all top-level decls.
+  auto Scope = D->getASTContext().getTraversalScope();
+  bool HasLimitedScope =
+      Scope.size() != 1 || !isa<TranslationUnitDecl>(Scope.front());
+  if (HasLimitedScope) {
+    ShouldVisitChildren = false; // we'll do that here instead
+    for (auto *Child : Scope) {
+      if (!canIgnoreChildDeclWhileTraversingDeclContext(Child))
+        TRY_TO(TraverseDecl(Child));
+    }
+  }
+})
+
+DEF_TRAVERSE_DECL(PragmaCommentDecl, {})
+
+DEF_TRAVERSE_DECL(PragmaDetectMismatchDecl, {})
+
+DEF_TRAVERSE_DECL(ExternCContextDecl, {})
+
+DEF_TRAVERSE_DECL(NamespaceAliasDecl, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+
+  // We shouldn't traverse an aliased namespace, since it will be
+  // defined (and, therefore, traversed) somewhere else.
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl.
+                             })
+
+DEF_TRAVERSE_DECL(
+    NamespaceDecl,
+    {// Code in an unnamed namespace shows up automatically in
+     // decls_begin()/decls_end().  Thus we don't need to recurse on
+     // D->getAnonymousNamespace().
+    })
+
+DEF_TRAVERSE_DECL(ObjCCompatibleAliasDecl, {// FIXME: implement
+                                           })
+
+DEF_TRAVERSE_DECL(ObjCCategoryDecl, {
+  if (ObjCTypeParamList *typeParamList = D->getTypeParamList()) {
+    for (auto typeParam : *typeParamList) {
+      TRY_TO(TraverseObjCTypeParamDecl(typeParam));
+    }
+  }
+  for (auto It : llvm::zip(D->protocols(), D->protocol_locs())) {
+    ObjCProtocolLoc ProtocolLoc(std::get<0>(It), std::get<1>(It));
+    TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+  }
+})
+
+DEF_TRAVERSE_DECL(ObjCCategoryImplDecl, {// FIXME: implement
+                                        })
+
+DEF_TRAVERSE_DECL(ObjCImplementationDecl, {// FIXME: implement
+                                          })
+
+DEF_TRAVERSE_DECL(ObjCInterfaceDecl, {
+  if (ObjCTypeParamList *typeParamList = D->getTypeParamListAsWritten()) {
+    for (auto typeParam : *typeParamList) {
+      TRY_TO(TraverseObjCTypeParamDecl(typeParam));
+    }
+  }
+
+  if (TypeSourceInfo *superTInfo = D->getSuperClassTInfo()) {
+    TRY_TO(TraverseTypeLoc(superTInfo->getTypeLoc()));
+  }
+  if (D->isThisDeclarationADefinition()) {
+    for (auto It : llvm::zip(D->protocols(), D->protocol_locs())) {
+      ObjCProtocolLoc ProtocolLoc(std::get<0>(It), std::get<1>(It));
+      TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+    }
+  }
+})
+
+DEF_TRAVERSE_DECL(ObjCProtocolDecl, {
+  if (D->isThisDeclarationADefinition()) {
+    for (auto It : llvm::zip(D->protocols(), D->protocol_locs())) {
+      ObjCProtocolLoc ProtocolLoc(std::get<0>(It), std::get<1>(It));
+      TRY_TO(TraverseObjCProtocolLoc(ProtocolLoc));
+    }
+  }
+})
+
+DEF_TRAVERSE_DECL(ObjCMethodDecl, {
+  if (D->getReturnTypeSourceInfo()) {
+    TRY_TO(TraverseTypeLoc(D->getReturnTypeSourceInfo()->getTypeLoc()));
+  }
+  for (ParmVarDecl *Parameter : D->parameters()) {
+    TRY_TO(TraverseDecl(Parameter));
+  }
+  if (D->isThisDeclarationADefinition()) {
+    TRY_TO(TraverseStmt(D->getBody()));
+  }
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(ObjCTypeParamDecl, {
+  if (D->hasExplicitBound()) {
+    TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+    // We shouldn't traverse D->getTypeForDecl(); it's a result of
+    // declaring the type alias, not something that was written in the
+    // source.
+  }
+})
+
+DEF_TRAVERSE_DECL(ObjCPropertyDecl, {
+  if (D->getTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+  else
+    TRY_TO(TraverseType(D->getType()));
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_DECL(UsingDecl, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+})
+
+DEF_TRAVERSE_DECL(UsingEnumDecl,
+                  { TRY_TO(TraverseTypeLoc(D->getEnumTypeLoc())); })
+
+DEF_TRAVERSE_DECL(UsingPackDecl, {})
+
+DEF_TRAVERSE_DECL(UsingDirectiveDecl, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+})
+
+DEF_TRAVERSE_DECL(UsingShadowDecl, {})
+
+DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})
+
+DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, {
+  for (auto *I : D->varlist()) {
+    TRY_TO(TraverseStmt(I));
+  }
+})
+
+DEF_TRAVERSE_DECL(OMPRequiresDecl, {
+  for (auto *C : D->clauselists()) {
+    TRY_TO(TraverseOMPClause(C));
+  }
+})
+
+DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, {
+  TRY_TO(TraverseStmt(D->getCombiner()));
+  if (auto *Initializer = D->getInitializer())
+    TRY_TO(TraverseStmt(Initializer));
+  TRY_TO(TraverseType(D->getType()));
+  return true;
+})
+
+DEF_TRAVERSE_DECL(OMPDeclareMapperDecl, {
+  for (auto *C : D->clauselists())
+    TRY_TO(TraverseOMPClause(C));
+  TRY_TO(TraverseType(D->getType()));
+  return true;
+})
+
+DEF_TRAVERSE_DECL(OMPCapturedExprDecl, { TRY_TO(TraverseVarHelper(D)); })
+
+DEF_TRAVERSE_DECL(OMPAllocateDecl, {
+  for (auto *I : D->varlist())
+    TRY_TO(TraverseStmt(I));
+  for (auto *C : D->clauselists())
+    TRY_TO(TraverseOMPClause(C));
+})
+
+DEF_TRAVERSE_DECL(OpenACCDeclareDecl,
+                  { TRY_TO(VisitOpenACCClauseList(D->clauses())); })
+
+DEF_TRAVERSE_DECL(OpenACCRoutineDecl, {
+  TRY_TO(TraverseStmt(D->getFunctionReference()));
+  TRY_TO(VisitOpenACCClauseList(D->clauses()));
+})
+
+// A helper method for TemplateDecl's children.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateParameterListHelper(
+    TemplateParameterList *TPL) {
+  if (TPL) {
+    for (NamedDecl *D : *TPL) {
+      TRY_TO(TraverseDecl(D));
+    }
+    if (Expr *RequiresClause = TPL->getRequiresClause()) {
+      TRY_TO(TraverseStmt(RequiresClause));
+    }
+  }
+  return true;
+}
+
+template <typename Derived>
+template <typename T>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseDeclTemplateParameterLists(T *D) {
+  for (unsigned i = 0; i < D->getNumTemplateParameterLists(); i++) {
+    TemplateParameterList *TPL = D->getTemplateParameterList(i);
+    TraverseTemplateParameterListHelper(TPL);
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateInstantiations(
+    ClassTemplateDecl *D) {
+  for (auto *SD : D->specializations()) {
+    for (auto *RD : SD->redecls()) {
+      assert(!cast<CXXRecordDecl>(RD)->isInjectedClassName());
+      switch (
+          cast<ClassTemplateSpecializationDecl>(RD)->getSpecializationKind()) {
+      // Visit the implicit instantiations with the requested pattern.
+      case TSK_Undeclared:
+      case TSK_ImplicitInstantiation:
+        TRY_TO(TraverseDecl(RD));
+        break;
+
+      // We don't need to do anything on an explicit instantiation
+      // or explicit specialization because there will be an explicit
+      // node for it elsewhere.
+      case TSK_ExplicitInstantiationDeclaration:
+      case TSK_ExplicitInstantiationDefinition:
+      case TSK_ExplicitSpecialization:
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateInstantiations(
+    VarTemplateDecl *D) {
+  for (auto *SD : D->specializations()) {
+    for (auto *RD : SD->redecls()) {
+      switch (
+          cast<VarTemplateSpecializationDecl>(RD)->getSpecializationKind()) {
+      case TSK_Undeclared:
+      case TSK_ImplicitInstantiation:
+        TRY_TO(TraverseDecl(RD));
+        break;
+
+      case TSK_ExplicitInstantiationDeclaration:
+      case TSK_ExplicitInstantiationDefinition:
+      case TSK_ExplicitSpecialization:
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+// A helper method for traversing the instantiations of a
+// function while skipping its specializations.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateInstantiations(
+    FunctionTemplateDecl *D) {
+  for (auto *FD : D->specializations()) {
+    for (auto *RD : FD->redecls()) {
+      switch (RD->getTemplateSpecializationKind()) {
+      case TSK_Undeclared:
+      case TSK_ImplicitInstantiation:
+        // We don't know what kind of FunctionDecl this is.
+        TRY_TO(TraverseDecl(RD));
+        break;
+
+      // FIXME: For now traverse explicit instantiations here. Change that
+      // once they are represented as dedicated nodes in the AST.
+      case TSK_ExplicitInstantiationDeclaration:
+      case TSK_ExplicitInstantiationDefinition:
+        TRY_TO(TraverseDecl(RD));
+        break;
+
+      case TSK_ExplicitSpecialization:
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+// This macro unifies the traversal of class, variable and function
+// template declarations.
+#define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND)                                   \
+  DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, {                              \
+    TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   \
+    TRY_TO(TraverseDecl(D->getTemplatedDecl()));                               \
+                                                                               \
+    /* By default, we do not traverse the instantiations of                    \
+       class templates since they do not appear in the user code. The          \
+       following code optionally traverses them.                               \
+                                                                               \
+       We only traverse the class instantiations when we see the canonical     \
+       declaration of the template, to ensure we only visit them once. */      \
+    if (getDerived().shouldVisitTemplateInstantiations() &&                    \
+        D == D->getCanonicalDecl())                                            \
+      TRY_TO(TraverseTemplateInstantiations(D));                               \
+                                                                               \
+    /* Note that getInstantiatedFromMemberTemplate() is just a link            \
+       from a template instantiation back to the template from which           \
+       it was instantiated, and thus should not be traversed. */               \
+  })
+
+DEF_TRAVERSE_TMPL_DECL(Class)
+DEF_TRAVERSE_TMPL_DECL(Var)
+DEF_TRAVERSE_TMPL_DECL(Function)
+
+DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, {
+  // D is the "T" in something like
+  //   template <template <typename> class T> class container { };
+  TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+  if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
+    TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+})
+
+DEF_TRAVERSE_DECL(BuiltinTemplateDecl, {
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+})
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints(
+    const TemplateTypeParmDecl *D) {
+  if (const auto *TC = D->getTypeConstraint())
+    TRY_TO(TraverseTypeConstraint(TC));
+  return true;
+}
+
+DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
+  // D is the "T" in something like "template<typename T> class vector;"
+  if (D->getTypeForDecl())
+    TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+  TRY_TO(TraverseTemplateTypeParamDeclConstraints(D));
+  if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
+    TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
+})
+
+DEF_TRAVERSE_DECL(TypedefDecl, {
+  TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+  // We shouldn't traverse D->getTypeForDecl(); it's a result of
+  // declaring the typedef, not something that was written in the
+  // source.
+})
+
+DEF_TRAVERSE_DECL(TypeAliasDecl, {
+  TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+  // We shouldn't traverse D->getTypeForDecl(); it's a result of
+  // declaring the type alias, not something that was written in the
+  // source.
+})
+
+DEF_TRAVERSE_DECL(TypeAliasTemplateDecl, {
+  TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+})
+
+DEF_TRAVERSE_DECL(ConceptDecl, {
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+  TRY_TO(TraverseStmt(D->getConstraintExpr()));
+})
+
+DEF_TRAVERSE_DECL(UnresolvedUsingTypenameDecl, {
+  // A dependent using declaration which was marked with 'typename'.
+  //   template<class T> class A : public B<T> { using typename B<T>::foo; };
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  // We shouldn't traverse D->getTypeForDecl(); it's a result of
+  // declaring the type, not something that was written in the
+  // source.
+})
+
+DEF_TRAVERSE_DECL(UnresolvedUsingIfExistsDecl, {})
+
+DEF_TRAVERSE_DECL(EnumDecl, {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
+
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  if (auto *TSI = D->getIntegerTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+  // The enumerators are already traversed by
+  // decls_begin()/decls_end().
+})
+
+// Helper methods for RecordDecl and its children.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseRecordHelper(RecordDecl *D) {
+  // We shouldn't traverse D->getTypeForDecl(); it's a result of
+  // declaring the type, not something that was written in the source.
+
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseCXXBaseSpecifier(
+    const CXXBaseSpecifier &Base) {
+  TRY_TO(TraverseTypeLoc(Base.getTypeSourceInfo()->getTypeLoc()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseCXXRecordHelper(CXXRecordDecl *D) {
+  if (!TraverseRecordHelper(D))
+    return false;
+  if (D->isCompleteDefinition()) {
+    for (const auto &I : D->bases()) {
+      TRY_TO(TraverseCXXBaseSpecifier(I));
+    }
+    // We don't traverse the friends or the conversions, as they are
+    // already in decls_begin()/decls_end().
+  }
+  return true;
+}
+
+DEF_TRAVERSE_DECL(RecordDecl, { TRY_TO(TraverseRecordHelper(D)); })
+
+DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseTemplateArgumentLocsHelper(
+    const TemplateArgumentLoc *TAL, unsigned Count) {
+  for (unsigned I = 0; I < Count; ++I) {
+    TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
+  }
+  return true;
+}
+
+#define DEF_TRAVERSE_TMPL_SPEC_DECL(TMPLDECLKIND, DECLKIND)                    \
+  DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateSpecializationDecl, {                \
+    /* For implicit instantiations ("set<int> x;"), we don't want to           \
+       recurse at all, since the instatiated template isn't written in         \
+       the source code anywhere.  (Note the instatiated *type* --              \
+       set<int> -- is written, and will still get a callback of                \
+       TemplateSpecializationType).  For explicit instantiations               \
+       ("template set<int>;"), we do need a callback, since this               \
+       is the only callback that's made for this instantiation.                \
+       We use getTemplateArgsAsWritten() to distinguish. */                    \
+    if (const auto *ArgsWritten = D->getTemplateArgsAsWritten()) {             \
+      /* The args that remains unspecialized. */                               \
+      TRY_TO(TraverseTemplateArgumentLocsHelper(                               \
+          ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs));      \
+    }                                                                          \
+                                                                               \
+    if (getDerived().shouldVisitTemplateInstantiations() ||                    \
+        D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {    \
+      /* Traverse base definition for explicit specializations */              \
+      TRY_TO(Traverse##DECLKIND##Helper(D));                                   \
+    } else {                                                                   \
+      TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));            \
+                                                                               \
+      /* Returning from here skips traversing the                              \
+         declaration context of the *TemplateSpecializationDecl                \
+         (embedded in the DEF_TRAVERSE_DECL() macro)                           \
+         which contains the instantiated members of the template. */           \
+      return true;                                                             \
+    }                                                                          \
+  })
+
+DEF_TRAVERSE_TMPL_SPEC_DECL(Class, CXXRecord)
+DEF_TRAVERSE_TMPL_SPEC_DECL(Var, Var)
+
+#define DEF_TRAVERSE_TMPL_PART_SPEC_DECL(TMPLDECLKIND, DECLKIND)               \
+  DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplatePartialSpecializationDecl, {         \
+    /* The partial specialization. */                                          \
+    TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   \
+    /* The args that remains unspecialized. */                                 \
+    TRY_TO(TraverseTemplateArgumentLocsHelper(                                 \
+        D->getTemplateArgsAsWritten()->getTemplateArgs(),                      \
+        D->getTemplateArgsAsWritten()->NumTemplateArgs));                      \
+                                                                               \
+    /* Don't need the *TemplatePartialSpecializationHelper, even               \
+       though that's our parent class -- we already visit all the              \
+       template args here. */                                                  \
+    TRY_TO(Traverse##DECLKIND##Helper(D));                                     \
+                                                                               \
+    /* Instantiations will have been visited with the primary template. */     \
+  })
+
+DEF_TRAVERSE_TMPL_PART_SPEC_DECL(Class, CXXRecord)
+DEF_TRAVERSE_TMPL_PART_SPEC_DECL(Var, Var)
+
+DEF_TRAVERSE_DECL(EnumConstantDecl, { TRY_TO(TraverseStmt(D->getInitExpr())); })
+
+DEF_TRAVERSE_DECL(UnresolvedUsingValueDecl, {
+  // Like UnresolvedUsingTypenameDecl, but without the 'typename':
+  //    template <class T> Class A : public Base<T> { using Base<T>::foo; };
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+})
+
+DEF_TRAVERSE_DECL(IndirectFieldDecl, {})
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseDeclaratorHelper(DeclaratorDecl *D) {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  if (D->getTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+  else
+    TRY_TO(TraverseType(D->getType()));
+  return true;
+}
+
+DEF_TRAVERSE_DECL(DecompositionDecl, {
+  TRY_TO(TraverseVarHelper(D));
+  for (auto *Binding : D->bindings()) {
+    TRY_TO(TraverseDecl(Binding));
+  }
+})
+
+DEF_TRAVERSE_DECL(BindingDecl, {
+  if (getDerived().shouldVisitImplicitCode()) {
+    TRY_TO(TraverseStmt(D->getBinding()));
+    if (const auto HoldingVar = D->getHoldingVar())
+      TRY_TO(TraverseDecl(HoldingVar));
+  }
+})
+
+DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); })
+
+DEF_TRAVERSE_DECL(MSGuidDecl, {})
+DEF_TRAVERSE_DECL(UnnamedGlobalConstantDecl, {})
+
+DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {})
+
+DEF_TRAVERSE_DECL(FieldDecl, {
+  TRY_TO(TraverseDeclaratorHelper(D));
+  if (D->isBitField())
+    TRY_TO(TraverseStmt(D->getBitWidth()));
+  if (D->hasInClassInitializer())
+    TRY_TO(TraverseStmt(D->getInClassInitializer()));
+})
+
+DEF_TRAVERSE_DECL(ObjCAtDefsFieldDecl, {
+  TRY_TO(TraverseDeclaratorHelper(D));
+  if (D->isBitField())
+    TRY_TO(TraverseStmt(D->getBitWidth()));
+  // FIXME: implement the rest.
+})
+
+DEF_TRAVERSE_DECL(ObjCIvarDecl, {
+  TRY_TO(TraverseDeclaratorHelper(D));
+  if (D->isBitField())
+    TRY_TO(TraverseStmt(D->getBitWidth()));
+  // FIXME: implement the rest.
+})
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
+  TRY_TO(TraverseDeclTemplateParameterLists(D));
+  TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+
+  // If we're an explicit template specialization, iterate over the
+  // template args that were explicitly specified.  If we were doing
+  // this in typing order, we'd do it between the return type and
+  // the function args, but both are handled by the FunctionTypeLoc
+  // above, so we have to choose one side.  I've decided to do before.
+  if (const FunctionTemplateSpecializationInfo *FTSI =
+          D->getTemplateSpecializationInfo()) {
+    if (FTSI->getTemplateSpecializationKind() != TSK_Undeclared &&
+        FTSI->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) {
+      // A specialization might not have explicit template arguments if it has
+      // a templated return type and concrete arguments.
+      if (const ASTTemplateArgumentListInfo *TALI =
+              FTSI->TemplateArgumentsAsWritten) {
+        TRY_TO(TraverseTemplateArgumentLocsHelper(TALI->getTemplateArgs(),
+                                                  TALI->NumTemplateArgs));
+      }
+    }
+  } else if (const DependentFunctionTemplateSpecializationInfo *DFSI =
+                 D->getDependentSpecializationInfo()) {
+    if (const ASTTemplateArgumentListInfo *TALI =
+            DFSI->TemplateArgumentsAsWritten) {
+      TRY_TO(TraverseTemplateArgumentLocsHelper(TALI->getTemplateArgs(),
+                                                TALI->NumTemplateArgs));
+    }
+  }
+
+  // Visit the function type itself, which can be either
+  // FunctionNoProtoType or FunctionProtoType, or a typedef.  This
+  // also covers the return type and the function parameters,
+  // including exception specifications.
+  if (TypeSourceInfo *TSI = D->getTypeSourceInfo()) {
+    TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+  } else if (getDerived().shouldVisitImplicitCode()) {
+    // Visit parameter variable declarations of the implicit function
+    // if the traverser is visiting implicit code. Parameter variable
+    // declarations do not have valid TypeSourceInfo, so to visit them
+    // we need to traverse the declarations explicitly.
+    for (ParmVarDecl *Parameter : D->parameters()) {
+      TRY_TO(TraverseDecl(Parameter));
+    }
+  }
+
+  // Visit the trailing requires clause, if any.
+  if (const AssociatedConstraint &TrailingRequiresClause =
+          D->getTrailingRequiresClause()) {
+    TRY_TO(TraverseStmt(
+        const_cast<Expr *>(TrailingRequiresClause.ConstraintExpr)));
+  }
+
+  if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
+    // Constructor initializers.
+    for (auto *I : Ctor->inits()) {
+      if (I->isWritten() || getDerived().shouldVisitImplicitCode())
+        TRY_TO(TraverseConstructorInitializer(I));
+    }
+  }
+
+  bool VisitBody =
+      D->isThisDeclarationADefinition() &&
+      // Don't visit the function body if the function definition is generated
+      // by clang.
+      (!D->isDefaulted() || getDerived().shouldVisitImplicitCode());
+
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+    if (const CXXRecordDecl *RD = MD->getParent()) {
+      if (RD->isLambda() &&
+          declaresSameEntity(RD->getLambdaCallOperator(), MD)) {
+        VisitBody = VisitBody && getDerived().shouldVisitLambdaBody();
+      }
+    }
+  }
+
+  if (VisitBody) {
+    TRY_TO(TraverseStmt(D->getBody()));
+    // Body may contain using declarations whose shadows are parented to the
+    // FunctionDecl itself.
+    for (auto *Child : D->decls()) {
+      if (isa<UsingShadowDecl>(Child))
+        TRY_TO(TraverseDecl(Child));
+    }
+  }
+  return true;
+}
+
+DEF_TRAVERSE_DECL(FunctionDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+DEF_TRAVERSE_DECL(CXXDeductionGuideDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+DEF_TRAVERSE_DECL(CXXMethodDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+DEF_TRAVERSE_DECL(CXXConstructorDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+// CXXConversionDecl is the declaration of a type conversion operator.
+// It's not a cast expression.
+DEF_TRAVERSE_DECL(CXXConversionDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+DEF_TRAVERSE_DECL(CXXDestructorDecl, {
+  // We skip decls_begin/decls_end, which are already covered by
+  // TraverseFunctionHelper().
+  ShouldVisitChildren = false;
+  ReturnValue = TraverseFunctionHelper(D);
+})
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseVarHelper(VarDecl *D) {
+  TRY_TO(TraverseDeclaratorHelper(D));
+  // Default params are taken care of when we traverse the ParmVarDecl.
+  if (!isa<ParmVarDecl>(D) &&
+      (!D->isCXXForRangeDecl() || getDerived().shouldVisitImplicitCode()))
+    TRY_TO(TraverseStmt(D->getInit()));
+  return true;
+}
+
+DEF_TRAVERSE_DECL(VarDecl, { TRY_TO(TraverseVarHelper(D)); })
+
+DEF_TRAVERSE_DECL(ImplicitParamDecl, { TRY_TO(TraverseVarHelper(D)); })
+
+DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, {
+  // A non-type template parameter, e.g. "S" in template<int S> class Foo ...
+  TRY_TO(TraverseDeclaratorHelper(D));
+  if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
+    TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
+})
+
+DEF_TRAVERSE_DECL(ParmVarDecl, {
+  TRY_TO(TraverseVarHelper(D));
+
+  if (D->hasDefaultArg() && D->hasUninstantiatedDefaultArg() &&
+      !D->hasUnparsedDefaultArg())
+    TRY_TO(TraverseStmt(D->getUninstantiatedDefaultArg()));
+
+  if (D->hasDefaultArg() && !D->hasUninstantiatedDefaultArg() &&
+      !D->hasUnparsedDefaultArg())
+    TRY_TO(TraverseStmt(D->getDefaultArg()));
+})
+
+DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {})
+
+DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, {
+  TRY_TO(TraverseTemplateArguments(D->getTemplateArguments()));
+})
+
+#undef DEF_TRAVERSE_DECL
+
+// ----------------- Stmt traversal -----------------
+//
+// For stmts, we automate (in the DEF_TRAVERSE_STMT macro) iterating
+// over the children defined in children() (every stmt defines these,
+// though sometimes the range is empty).  Each individual Traverse*
+// method only needs to worry about children other than those.  To see
+// what children() does for a given class, see, e.g.,
+//   http://clang.llvm.org/doxygen/Stmt_8cpp_source.html
+
+// This macro makes available a variable S, the passed-in stmt.
+#define DEF_TRAVERSE_STMT(STMT, CODE)                                          \
+  template <typename Derived>                                                  \
+  bool RecursiveASTEnterExitVisitor<Derived>::Traverse##STMT(                           \
+      STMT *S, DataRecursionQueue *Queue) {                                    \
+    bool ShouldVisitChildren = true;                                           \
+    bool ReturnValue = true;                                                   \
+    if (!getDerived().shouldTraversePostOrder())                               \
+      TRY_TO(WalkUpFrom##STMT(S));                                             \
+    { CODE; }                                                                  \
+    if (ShouldVisitChildren) {                                                 \
+      for (Stmt * SubStmt : getDerived().getStmtChildren(S)) {                 \
+        TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt);                              \
+      }                                                                        \
+    }                                                                          \
+    /* Call WalkUpFrom if TRY_TO_TRAVERSE_OR_ENQUEUE_STMT has traversed the    \
+     * children already. If TRY_TO_TRAVERSE_OR_ENQUEUE_STMT only enqueued the  \
+     * children, PostVisitStmt will call WalkUpFrom after we are done visiting \
+     * children. */                                                            \
+    if (!Queue && ReturnValue && getDerived().shouldTraversePostOrder()) {     \
+      TRY_TO(WalkUpFrom##STMT(S));                                             \
+    }                                                                          \
+    return ReturnValue;                                                        \
+  }
+
+DEF_TRAVERSE_STMT(GCCAsmStmt, {
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmStringExpr());
+  for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintExpr(I));
+  }
+  for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintExpr(I));
+  }
+  for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberExpr(I));
+  }
+  // children() iterates over inputExpr and outputExpr.
+})
+
+DEF_TRAVERSE_STMT(
+    MSAsmStmt,
+    {// FIXME: MS Asm doesn't currently parse Constraints, Clobbers, etc.  Once
+     // added this needs to be implemented.
+    })
+
+DEF_TRAVERSE_STMT(CXXCatchStmt, {
+  TRY_TO(TraverseDecl(S->getExceptionDecl()));
+  // children() iterates over the handler block.
+})
+
+DEF_TRAVERSE_STMT(DeclStmt, {
+  for (auto *I : S->decls()) {
+    TRY_TO(TraverseDecl(I));
+  }
+  // Suppress the default iteration over children() by
+  // returning.  Here's why: A DeclStmt looks like 'type var [=
+  // initializer]'.  The decls above already traverse over the
+  // initializers, so we don't have to do it again (which
+  // children() would do).
+  ShouldVisitChildren = false;
+})
+
+// These non-expr stmts (most of them), do not need any action except
+// iterating over the children.
+DEF_TRAVERSE_STMT(BreakStmt, {})
+DEF_TRAVERSE_STMT(CXXTryStmt, {})
+DEF_TRAVERSE_STMT(CaseStmt, {})
+DEF_TRAVERSE_STMT(CompoundStmt, {})
+DEF_TRAVERSE_STMT(ContinueStmt, {})
+DEF_TRAVERSE_STMT(DefaultStmt, {})
+DEF_TRAVERSE_STMT(DoStmt, {})
+DEF_TRAVERSE_STMT(ForStmt, {})
+DEF_TRAVERSE_STMT(GotoStmt, {})
+DEF_TRAVERSE_STMT(IfStmt, {})
+DEF_TRAVERSE_STMT(IndirectGotoStmt, {})
+DEF_TRAVERSE_STMT(LabelStmt, {})
+DEF_TRAVERSE_STMT(AttributedStmt, {})
+DEF_TRAVERSE_STMT(NullStmt, {})
+DEF_TRAVERSE_STMT(ObjCAtCatchStmt, {})
+DEF_TRAVERSE_STMT(ObjCAtFinallyStmt, {})
+DEF_TRAVERSE_STMT(ObjCAtSynchronizedStmt, {})
+DEF_TRAVERSE_STMT(ObjCAtThrowStmt, {})
+DEF_TRAVERSE_STMT(ObjCAtTryStmt, {})
+DEF_TRAVERSE_STMT(ObjCForCollectionStmt, {})
+DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, {})
+
+DEF_TRAVERSE_STMT(CXXForRangeStmt, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    if (S->getInit())
+      TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInit());
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getLoopVarStmt());
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRangeInit());
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
+    // Visit everything else only if shouldVisitImplicitCode().
+    ShouldVisitChildren = false;
+  }
+})
+
+DEF_TRAVERSE_STMT(MSDependentExistsStmt, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+})
+
+DEF_TRAVERSE_STMT(ReturnStmt, {})
+DEF_TRAVERSE_STMT(SwitchStmt, {})
+DEF_TRAVERSE_STMT(WhileStmt, {})
+
+DEF_TRAVERSE_STMT(ConstantExpr, {})
+
+DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(S->getMemberNameInfo()));
+  if (S->hasExplicitTemplateArgs()) {
+    TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                              S->getNumTemplateArgs()));
+  }
+})
+
+DEF_TRAVERSE_STMT(DeclRefExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+  TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                            S->getNumTemplateArgs()));
+})
+
+DEF_TRAVERSE_STMT(DependentScopeDeclRefExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+  if (S->hasExplicitTemplateArgs()) {
+    TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                              S->getNumTemplateArgs()));
+  }
+})
+
+DEF_TRAVERSE_STMT(MemberExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(S->getMemberNameInfo()));
+  TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                            S->getNumTemplateArgs()));
+})
+
+DEF_TRAVERSE_STMT(
+    ImplicitCastExpr,
+    {// We don't traverse the cast type, as it's not written in the
+     // source code.
+    })
+
+DEF_TRAVERSE_STMT(CStyleCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXFunctionalCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXAddrspaceCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXConstCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXDynamicCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXReinterpretCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXStaticCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(BuiltinBitCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseSynOrSemInitListExpr(
+    InitListExpr *S, DataRecursionQueue *Queue) {
+  if (S) {
+    // Skip this if we traverse postorder. We will visit it later
+    // in PostVisitStmt.
+    if (!getDerived().shouldTraversePostOrder())
+      TRY_TO(WalkUpFromInitListExpr(S));
+
+    // All we need are the default actions.  FIXME: use a helper function.
+    for (Stmt *SubStmt : S->children()) {
+      TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt);
+    }
+
+    if (!Queue && getDerived().shouldTraversePostOrder())
+      TRY_TO(WalkUpFromInitListExpr(S));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseObjCProtocolLoc(
+    ObjCProtocolLoc ProtocolLoc) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseConceptReference(
+    ConceptReference *CR) {
+  if (!getDerived().shouldTraversePostOrder())
+    TRY_TO(VisitConceptReference(CR));
+  TRY_TO(TraverseNestedNameSpecifierLoc(CR->getNestedNameSpecifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(CR->getConceptNameInfo()));
+  if (CR->hasExplicitTemplateArgs())
+    TRY_TO(TraverseTemplateArgumentLocsHelper(
+        CR->getTemplateArgsAsWritten()->getTemplateArgs(),
+        CR->getTemplateArgsAsWritten()->NumTemplateArgs));
+  if (getDerived().shouldTraversePostOrder())
+    TRY_TO(VisitConceptReference(CR));
+  return true;
+}
+
+// If shouldVisitImplicitCode() returns false, this method traverses only the
+// syntactic form of InitListExpr.
+// If shouldVisitImplicitCode() return true, this method is called once for
+// each pair of syntactic and semantic InitListExpr, and it traverses the
+// subtrees defined by the two forms. This may cause some of the children to be
+// visited twice, if they appear both in the syntactic and the semantic form.
+//
+// There is no guarantee about which form \p S takes when this method is called.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseInitListExpr(
+    InitListExpr *S, DataRecursionQueue *Queue) {
+  if (S->isSemanticForm() && S->isSyntacticForm()) {
+    // `S` does not have alternative forms, traverse only once.
+    TRY_TO(TraverseSynOrSemInitListExpr(S, Queue));
+    return true;
+  }
+  TRY_TO(TraverseSynOrSemInitListExpr(
+      S->isSemanticForm() ? S->getSyntacticForm() : S, Queue));
+  if (getDerived().shouldVisitImplicitCode()) {
+    // Only visit the semantic form if the clients are interested in implicit
+    // compiler-generated.
+    TRY_TO(TraverseSynOrSemInitListExpr(
+        S->isSemanticForm() ? S : S->getSemanticForm(), Queue));
+  }
+  return true;
+}
+
+// GenericSelectionExpr is a special case because the types and expressions
+// are interleaved.  We also need to watch out for null types (default
+// generic associations).
+DEF_TRAVERSE_STMT(GenericSelectionExpr, {
+  if (S->isExprPredicate())
+    TRY_TO(TraverseStmt(S->getControllingExpr()));
+  else
+    TRY_TO(TraverseTypeLoc(S->getControllingType()->getTypeLoc()));
+
+  for (const GenericSelectionExpr::Association Assoc : S->associations()) {
+    if (TypeSourceInfo *TSI = Assoc.getTypeSourceInfo())
+      TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(Assoc.getAssociationExpr());
+  }
+  ShouldVisitChildren = false;
+})
+
+// PseudoObjectExpr is a special case because of the weirdness with
+// syntactic expressions and opaque values.
+DEF_TRAVERSE_STMT(PseudoObjectExpr, {
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getSyntacticForm());
+  for (PseudoObjectExpr::semantics_iterator i = S->semantics_begin(),
+                                            e = S->semantics_end();
+       i != e; ++i) {
+    Expr *sub = *i;
+    if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
+      sub = OVE->getSourceExpr();
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(sub);
+  }
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, {
+  // This is called for code like 'return T()' where T is a built-in
+  // (i.e. non-class) type.
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXNewExpr, {
+  // The child-iterator will pick up the other arguments.
+  TRY_TO(TraverseTypeLoc(S->getAllocatedTypeSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(OffsetOfExpr, {
+  // The child-iterator will pick up the expression representing
+  // the field.
+  // FIMXE: for code like offsetof(Foo, a.b.c), should we get
+  // making a MemberExpr callbacks for Foo.a, Foo.a.b, and Foo.a.b.c?
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(UnaryExprOrTypeTraitExpr, {
+  // The child-iterator will pick up the arg if it's an expression,
+  // but not if it's a type.
+  if (S->isArgumentType())
+    TRY_TO(TraverseTypeLoc(S->getArgumentTypeInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXTypeidExpr, {
+  // The child-iterator will pick up the arg if it's an expression,
+  // but not if it's a type.
+  if (S->isTypeOperand())
+    TRY_TO(TraverseTypeLoc(S->getTypeOperandSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(MSPropertyRefExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+})
+
+DEF_TRAVERSE_STMT(MSPropertySubscriptExpr, {})
+
+DEF_TRAVERSE_STMT(CXXUuidofExpr, {
+  // The child-iterator will pick up the arg if it's an expression,
+  // but not if it's a type.
+  if (S->isTypeOperand())
+    TRY_TO(TraverseTypeLoc(S->getTypeOperandSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(TypeTraitExpr, {
+  for (unsigned I = 0, N = S->getNumArgs(); I != N; ++I)
+    TRY_TO(TraverseTypeLoc(S->getArg(I)->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ArrayTypeTraitExpr, {
+  TRY_TO(TraverseTypeLoc(S->getQueriedTypeSourceInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ExpressionTraitExpr,
+                  { TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getQueriedExpression()); })
+
+DEF_TRAVERSE_STMT(VAArgExpr, {
+  // The child-iterator will pick up the expression argument.
+  TRY_TO(TraverseTypeLoc(S->getWrittenTypeInfo()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, {
+  // This is called for code like 'return T()' where T is a class type.
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+
+// Walk only the visible parts of lambda expressions.
+DEF_TRAVERSE_STMT(LambdaExpr, {
+  // Visit the capture list.
+  for (unsigned I = 0, N = S->capture_size(); I != N; ++I) {
+    const LambdaCapture *C = S->capture_begin() + I;
+    if (C->isExplicit() || getDerived().shouldVisitImplicitCode()) {
+      TRY_TO(TraverseLambdaCapture(S, C, S->capture_init_begin()[I]));
+    }
+  }
+
+  if (getDerived().shouldVisitImplicitCode()) {
+    // The implicit model is simple: everything else is in the lambda class.
+    TRY_TO(TraverseDecl(S->getLambdaClass()));
+  } else {
+    // We need to poke around to find the bits that might be explicitly written.
+    TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
+    FunctionProtoTypeLoc Proto = TL.getAsAdjusted<FunctionProtoTypeLoc>();
+
+    TRY_TO(TraverseTemplateParameterListHelper(S->getTemplateParameterList()));
+    if (S->hasExplicitParameters()) {
+      // Visit parameters.
+      for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I)
+        TRY_TO(TraverseDecl(Proto.getParam(I)));
+    }
+
+    auto *T = Proto.getTypePtr();
+    for (const auto &E : T->exceptions())
+      TRY_TO(TraverseType(E));
+
+    if (Expr *NE = T->getNoexceptExpr())
+      TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(NE);
+
+    if (S->hasExplicitResultType())
+      TRY_TO(TraverseTypeLoc(Proto.getReturnLoc()));
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(
+        const_cast<Expr *>(S->getTrailingRequiresClause().ConstraintExpr));
+
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
+  }
+  ShouldVisitChildren = false;
+})
+
+DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
+  // This is called for code like 'T()', where T is a template argument.
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+
+// These expressions all might take explicit template arguments.
+// We traverse those if so.  FIXME: implement these.
+DEF_TRAVERSE_STMT(CXXConstructExpr, {})
+DEF_TRAVERSE_STMT(CallExpr, {})
+DEF_TRAVERSE_STMT(CXXMemberCallExpr, {})
+
+// These exprs (most of them), do not need any action except iterating
+// over the children.
+DEF_TRAVERSE_STMT(AddrLabelExpr, {})
+DEF_TRAVERSE_STMT(ArraySubscriptExpr, {})
+DEF_TRAVERSE_STMT(MatrixSubscriptExpr, {})
+DEF_TRAVERSE_STMT(ArraySectionExpr, {})
+DEF_TRAVERSE_STMT(OMPArrayShapingExpr, {})
+DEF_TRAVERSE_STMT(OMPIteratorExpr, {})
+
+DEF_TRAVERSE_STMT(BlockExpr, {
+  TRY_TO(TraverseDecl(S->getBlockDecl()));
+  return true; // no child statements to loop through.
+})
+
+DEF_TRAVERSE_STMT(ChooseExpr, {})
+DEF_TRAVERSE_STMT(CompoundLiteralExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, {})
+DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, {})
+
+DEF_TRAVERSE_STMT(CXXDefaultArgExpr, {
+  if (getDerived().shouldVisitImplicitCode())
+    TRY_TO(TraverseStmt(S->getExpr()));
+})
+
+DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {
+  if (getDerived().shouldVisitImplicitCode())
+    TRY_TO(TraverseStmt(S->getExpr()));
+})
+
+DEF_TRAVERSE_STMT(CXXDeleteExpr, {})
+DEF_TRAVERSE_STMT(ExprWithCleanups, {})
+DEF_TRAVERSE_STMT(CXXInheritedCtorInitExpr, {})
+DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, {})
+DEF_TRAVERSE_STMT(CXXStdInitializerListExpr, {})
+
+DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  if (TypeSourceInfo *ScopeInfo = S->getScopeTypeInfo())
+    TRY_TO(TraverseTypeLoc(ScopeInfo->getTypeLoc()));
+  if (TypeSourceInfo *DestroyedTypeInfo = S->getDestroyedTypeInfo())
+    TRY_TO(TraverseTypeLoc(DestroyedTypeInfo->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(CXXThisExpr, {})
+DEF_TRAVERSE_STMT(CXXThrowExpr, {})
+DEF_TRAVERSE_STMT(UserDefinedLiteral, {})
+DEF_TRAVERSE_STMT(DesignatedInitExpr, {})
+DEF_TRAVERSE_STMT(DesignatedInitUpdateExpr, {})
+DEF_TRAVERSE_STMT(ExtVectorElementExpr, {})
+DEF_TRAVERSE_STMT(GNUNullExpr, {})
+DEF_TRAVERSE_STMT(ImplicitValueInitExpr, {})
+DEF_TRAVERSE_STMT(NoInitExpr, {})
+DEF_TRAVERSE_STMT(ArrayInitLoopExpr, {
+  // FIXME: The source expression of the OVE should be listed as
+  // a child of the ArrayInitLoopExpr.
+  if (OpaqueValueExpr *OVE = S->getCommonExpr())
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(OVE->getSourceExpr());
+})
+DEF_TRAVERSE_STMT(ArrayInitIndexExpr, {})
+DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, {})
+
+DEF_TRAVERSE_STMT(ObjCEncodeExpr, {
+  if (TypeSourceInfo *TInfo = S->getEncodedTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ObjCIsaExpr, {})
+DEF_TRAVERSE_STMT(ObjCIvarRefExpr, {})
+
+DEF_TRAVERSE_STMT(ObjCMessageExpr, {
+  if (TypeSourceInfo *TInfo = S->getClassReceiverTypeInfo())
+    TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, {
+  if (S->isClassReceiver()) {
+    ObjCInterfaceDecl *IDecl = S->getClassReceiver();
+    QualType Type = IDecl->getASTContext().getObjCInterfaceType(IDecl);
+    ObjCInterfaceLocInfo Data;
+    Data.NameLoc = S->getReceiverLocation();
+    Data.NameEndLoc = Data.NameLoc;
+    TRY_TO(TraverseTypeLoc(TypeLoc(Type, &Data)));
+  }
+})
+DEF_TRAVERSE_STMT(ObjCSubscriptRefExpr, {})
+DEF_TRAVERSE_STMT(ObjCProtocolExpr, {})
+DEF_TRAVERSE_STMT(ObjCSelectorExpr, {})
+DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, {})
+
+DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
+DEF_TRAVERSE_STMT(ParenExpr, {})
+DEF_TRAVERSE_STMT(ParenListExpr, {})
+DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
+DEF_TRAVERSE_STMT(OpenACCAsteriskSizeExpr, {})
+DEF_TRAVERSE_STMT(PredefinedExpr, {})
+DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
+DEF_TRAVERSE_STMT(ConvertVectorExpr, {})
+DEF_TRAVERSE_STMT(StmtExpr, {})
+DEF_TRAVERSE_STMT(SourceLocExpr, {})
+DEF_TRAVERSE_STMT(EmbedExpr, {
+  for (IntegerLiteral *IL : S->underlying_data_elements()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(IL);
+  }
+})
+
+DEF_TRAVERSE_STMT(UnresolvedLookupExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  if (S->hasExplicitTemplateArgs()) {
+    TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                              S->getNumTemplateArgs()));
+  }
+})
+
+DEF_TRAVERSE_STMT(UnresolvedMemberExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+  if (S->hasExplicitTemplateArgs()) {
+    TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+                                              S->getNumTemplateArgs()));
+  }
+})
+
+DEF_TRAVERSE_STMT(SEHTryStmt, {})
+DEF_TRAVERSE_STMT(SEHExceptStmt, {})
+DEF_TRAVERSE_STMT(SEHFinallyStmt, {})
+DEF_TRAVERSE_STMT(SEHLeaveStmt, {})
+DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); })
+
+DEF_TRAVERSE_STMT(SYCLKernelCallStmt, {
+  if (getDerived().shouldVisitImplicitCode()) {
+    TRY_TO(TraverseStmt(S->getOriginalStmt()));
+    TRY_TO(TraverseDecl(S->getOutlinedFunctionDecl()));
+    ShouldVisitChildren = false;
+  }
+})
+
+DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {})
+DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+        S->getDecomposedForm();
+    TRY_TO(TraverseStmt(const_cast<Expr*>(Decomposed.LHS)));
+    TRY_TO(TraverseStmt(const_cast<Expr*>(Decomposed.RHS)));
+    ShouldVisitChildren = false;
+  }
+})
+DEF_TRAVERSE_STMT(OpaqueValueExpr, {})
+DEF_TRAVERSE_STMT(TypoExpr, {})
+DEF_TRAVERSE_STMT(RecoveryExpr, {})
+DEF_TRAVERSE_STMT(CUDAKernelCallExpr, {})
+
+// These operators (all of them) do not need any action except
+// iterating over the children.
+DEF_TRAVERSE_STMT(BinaryConditionalOperator, {})
+DEF_TRAVERSE_STMT(ConditionalOperator, {})
+DEF_TRAVERSE_STMT(UnaryOperator, {})
+DEF_TRAVERSE_STMT(BinaryOperator, {})
+DEF_TRAVERSE_STMT(CompoundAssignOperator, {})
+DEF_TRAVERSE_STMT(CXXNoexceptExpr, {})
+DEF_TRAVERSE_STMT(PackExpansionExpr, {})
+DEF_TRAVERSE_STMT(SizeOfPackExpr, {})
+DEF_TRAVERSE_STMT(PackIndexingExpr, {})
+DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, {})
+DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {})
+DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
+DEF_TRAVERSE_STMT(CXXFoldExpr, {})
+DEF_TRAVERSE_STMT(AtomicExpr, {})
+DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
+
+DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
+  if (S->getLifetimeExtendedTemporaryDecl()) {
+    TRY_TO(TraverseLifetimeExtendedTemporaryDecl(
+        S->getLifetimeExtendedTemporaryDecl()));
+    ShouldVisitChildren = false;
+  }
+})
+// For coroutines expressions, traverse either the operand
+// as written or the implied calls, depending on what the
+// derived class requests.
+DEF_TRAVERSE_STMT(CoroutineBodyStmt, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
+    ShouldVisitChildren = false;
+  }
+})
+DEF_TRAVERSE_STMT(CoreturnStmt, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
+    ShouldVisitChildren = false;
+  }
+})
+DEF_TRAVERSE_STMT(CoawaitExpr, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
+    ShouldVisitChildren = false;
+  }
+})
+DEF_TRAVERSE_STMT(DependentCoawaitExpr, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
+    ShouldVisitChildren = false;
+  }
+})
+DEF_TRAVERSE_STMT(CoyieldExpr, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
+    ShouldVisitChildren = false;
+  }
+})
+
+DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
+  TRY_TO(TraverseConceptReference(S->getConceptReference()));
+})
+
+DEF_TRAVERSE_STMT(RequiresExpr, {
+  TRY_TO(TraverseDecl(S->getBody()));
+  for (ParmVarDecl *Parm : S->getLocalParameters())
+    TRY_TO(TraverseDecl(Parm));
+  for (concepts::Requirement *Req : S->getRequirements())
+    TRY_TO(TraverseConceptRequirement(Req));
+})
+
+// These literals (all of them) do not need any action.
+DEF_TRAVERSE_STMT(IntegerLiteral, {})
+DEF_TRAVERSE_STMT(FixedPointLiteral, {})
+DEF_TRAVERSE_STMT(CharacterLiteral, {})
+DEF_TRAVERSE_STMT(FloatingLiteral, {})
+DEF_TRAVERSE_STMT(ImaginaryLiteral, {})
+DEF_TRAVERSE_STMT(StringLiteral, {})
+DEF_TRAVERSE_STMT(ObjCStringLiteral, {})
+DEF_TRAVERSE_STMT(ObjCBoxedExpr, {})
+DEF_TRAVERSE_STMT(ObjCArrayLiteral, {})
+DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, {})
+
+// Traverse OpenCL: AsType, Convert.
+DEF_TRAVERSE_STMT(AsTypeExpr, {})
+
+// OpenMP directives.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseOMPExecutableDirective(
+    OMPExecutableDirective *S) {
+  for (auto *C : S->clauses()) {
+    TRY_TO(TraverseOMPClause(C));
+  }
+  return true;
+}
+
+DEF_TRAVERSE_STMT(OMPCanonicalLoop, {
+  if (!getDerived().shouldVisitImplicitCode()) {
+    // Visit only the syntactical loop.
+    TRY_TO(TraverseStmt(S->getLoopStmt()));
+    ShouldVisitChildren = false;
+  }
+})
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::TraverseOMPLoopDirective(OMPLoopDirective *S) {
+  return TraverseOMPExecutableDirective(S);
+}
+
+DEF_TRAVERSE_STMT(OMPMetaDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTileDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPStripeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPUnrollDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPReverseDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPInterchangeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPSectionsDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPSectionDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPScopeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPSingleDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMasterDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPCriticalDirective, {
+  TRY_TO(TraverseDeclarationNameInfo(S->getDirectiveName()));
+  TRY_TO(TraverseOMPExecutableDirective(S));
+})
+
+DEF_TRAVERSE_STMT(OMPParallelForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMasterDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMaskedDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelSectionsDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskyieldDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPBarrierDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskwaitDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskgroupDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPCancellationPointDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPCancelDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPFlushDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDepobjDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPScanDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPOrderedDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPAtomicDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetDataDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetEnterDataDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetExitDataDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetParallelDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetParallelForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetUpdateDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTaskLoopSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMasterTaskLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMasterTaskLoopSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMasterTaskLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMasterTaskLoopSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMaskedTaskLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMaskedTaskLoopSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMaskedTaskLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelMaskedTaskLoopSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDistributeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDistributeParallelForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDistributeParallelForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDistributeSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetParallelForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsDistributeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsDistributeSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsDistributeParallelForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsDistributeParallelForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsDistributeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsDistributeParallelForDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsDistributeParallelForSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsDistributeSimdDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPInteropDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPDispatchDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPMaskedDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPGenericLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTeamsGenericLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetTeamsGenericLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPParallelGenericLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPTargetParallelGenericLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPAssumeDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPErrorDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+// OpenMP clauses.
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseOMPClause(OMPClause *C) {
+  if (!C)
+    return true;
+  switch (C->getClauseKind()) {
+#define GEN_CLANG_CLAUSE_CLASS
+#define CLAUSE_CLASS(Enum, Str, Class)                                         \
+  case llvm::omp::Clause::Enum:                                                \
+    TRY_TO(Visit##Class(static_cast<Class *>(C)));                             \
+    break;
+#define CLAUSE_NO_CLASS(Enum, Str)                                             \
+  case llvm::omp::Clause::Enum:                                                \
+    break;
+#include "llvm/Frontend/OpenMP/OMP.inc"
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPClauseWithPreInit(
+    OMPClauseWithPreInit *Node) {
+  TRY_TO(TraverseStmt(Node->getPreInitStmt()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPClauseWithPostUpdate(
+    OMPClauseWithPostUpdate *Node) {
+  TRY_TO(VisitOMPClauseWithPreInit(Node));
+  TRY_TO(TraverseStmt(Node->getPostUpdateExpr()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAllocatorClause(
+    OMPAllocatorClause *C) {
+  TRY_TO(TraverseStmt(C->getAllocator()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAllocateClause(OMPAllocateClause *C) {
+  TRY_TO(TraverseStmt(C->getAllocator()));
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPIfClause(OMPIfClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getCondition()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFinalClause(OMPFinalClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getCondition()));
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getNumThreads()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAlignClause(OMPAlignClause *C) {
+  TRY_TO(TraverseStmt(C->getAlignment()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSafelenClause(OMPSafelenClause *C) {
+  TRY_TO(TraverseStmt(C->getSafelen()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSimdlenClause(OMPSimdlenClause *C) {
+  TRY_TO(TraverseStmt(C->getSimdlen()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSizesClause(OMPSizesClause *C) {
+  for (Expr *E : C->getSizesRefs())
+    TRY_TO(TraverseStmt(E));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPPermutationClause(
+    OMPPermutationClause *C) {
+  for (Expr *E : C->getArgsRefs())
+    TRY_TO(TraverseStmt(E));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFullClause(OMPFullClause *C) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPPartialClause(OMPPartialClause *C) {
+  TRY_TO(TraverseStmt(C->getFactor()));
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPCollapseClause(OMPCollapseClause *C) {
+  TRY_TO(TraverseStmt(C->getNumForLoops()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDefaultClause(OMPDefaultClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPProcBindClause(OMPProcBindClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUnifiedAddressClause(
+    OMPUnifiedAddressClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUnifiedSharedMemoryClause(
+    OMPUnifiedSharedMemoryClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPReverseOffloadClause(
+    OMPReverseOffloadClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDynamicAllocatorsClause(
+    OMPDynamicAllocatorsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAtomicDefaultMemOrderClause(
+    OMPAtomicDefaultMemOrderClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSelfMapsClause(OMPSelfMapsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAtClause(OMPAtClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSeverityClause(OMPSeverityClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPMessageClause(OMPMessageClause *C) {
+  TRY_TO(TraverseStmt(C->getMessageString()));
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPScheduleClause(OMPScheduleClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getChunkSize()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPOrderedClause(OMPOrderedClause *C) {
+  TRY_TO(TraverseStmt(C->getNumForLoops()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNowaitClause(OMPNowaitClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUntiedClause(OMPUntiedClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPMergeableClause(OMPMergeableClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPReadClause(OMPReadClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPWriteClause(OMPWriteClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUpdateClause(OMPUpdateClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPCaptureClause(OMPCaptureClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPCompareClause(OMPCompareClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFailClause(OMPFailClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSeqCstClause(OMPSeqCstClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAcqRelClause(OMPAcqRelClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAbsentClause(OMPAbsentClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPHoldsClause(OMPHoldsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPContainsClause(OMPContainsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNoOpenMPClause(OMPNoOpenMPClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNoOpenMPRoutinesClause(
+    OMPNoOpenMPRoutinesClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNoOpenMPConstructsClause(
+    OMPNoOpenMPConstructsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNoParallelismClause(
+    OMPNoParallelismClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAcquireClause(OMPAcquireClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPReleaseClause(OMPReleaseClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPRelaxedClause(OMPRelaxedClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPWeakClause(OMPWeakClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPThreadsClause(OMPThreadsClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSIMDClause(OMPSIMDClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNogroupClause(OMPNogroupClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPInitClause(OMPInitClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUseClause(OMPUseClause *C) {
+  TRY_TO(TraverseStmt(C->getInteropVar()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDestroyClause(OMPDestroyClause *C) {
+  TRY_TO(TraverseStmt(C->getInteropVar()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNovariantsClause(
+    OMPNovariantsClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getCondition()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNocontextClause(
+    OMPNocontextClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getCondition()));
+  return true;
+}
+
+template <typename Derived>
+template <typename T>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPClauseList(T *Node) {
+  for (auto *E : Node->varlist()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPInclusiveClause(
+    OMPInclusiveClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPExclusiveClause(
+    OMPExclusiveClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPPrivateClause(OMPPrivateClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  for (auto *E : C->private_copies()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFirstprivateClause(
+    OMPFirstprivateClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  for (auto *E : C->private_copies()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->inits()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPLastprivateClause(
+    OMPLastprivateClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPostUpdate(C));
+  for (auto *E : C->private_copies()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->source_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->destination_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->assignment_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPSharedClause(OMPSharedClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPLinearClause(OMPLinearClause *C) {
+  TRY_TO(TraverseStmt(C->getStep()));
+  TRY_TO(TraverseStmt(C->getCalcStep()));
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPostUpdate(C));
+  for (auto *E : C->privates()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->inits()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->updates()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->finals()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAlignedClause(OMPAlignedClause *C) {
+  TRY_TO(TraverseStmt(C->getAlignment()));
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPCopyinClause(OMPCopyinClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  for (auto *E : C->source_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->destination_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->assignment_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPCopyprivateClause(
+    OMPCopyprivateClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  for (auto *E : C->source_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->destination_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->assignment_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPReductionClause(OMPReductionClause *C) {
+  TRY_TO(TraverseNestedNameSpecifierLoc(C->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(C->getNameInfo()));
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPostUpdate(C));
+  for (auto *E : C->privates()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->lhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->rhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->reduction_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  if (C->getModifier() == OMPC_REDUCTION_inscan) {
+    for (auto *E : C->copy_ops()) {
+      TRY_TO(TraverseStmt(E));
+    }
+    for (auto *E : C->copy_array_temps()) {
+      TRY_TO(TraverseStmt(E));
+    }
+    for (auto *E : C->copy_array_elems()) {
+      TRY_TO(TraverseStmt(E));
+    }
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPTaskReductionClause(
+    OMPTaskReductionClause *C) {
+  TRY_TO(TraverseNestedNameSpecifierLoc(C->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(C->getNameInfo()));
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPostUpdate(C));
+  for (auto *E : C->privates()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->lhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->rhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->reduction_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPInReductionClause(
+    OMPInReductionClause *C) {
+  TRY_TO(TraverseNestedNameSpecifierLoc(C->getQualifierLoc()));
+  TRY_TO(TraverseDeclarationNameInfo(C->getNameInfo()));
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPostUpdate(C));
+  for (auto *E : C->privates()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->lhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->rhs_exprs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->reduction_ops()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  for (auto *E : C->taskgroup_descriptors())
+    TRY_TO(TraverseStmt(E));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFlushClause(OMPFlushClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDepobjClause(OMPDepobjClause *C) {
+  TRY_TO(TraverseStmt(C->getDepobj()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDependClause(OMPDependClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDeviceClause(OMPDeviceClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getDevice()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPMapClause(OMPMapClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNumTeamsClause(
+    OMPNumTeamsClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPThreadLimitClause(
+    OMPThreadLimitClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPPriorityClause(
+    OMPPriorityClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getPriority()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPGrainsizeClause(
+    OMPGrainsizeClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getGrainsize()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNumTasksClause(
+    OMPNumTasksClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getNumTasks()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPHintClause(OMPHintClause *C) {
+  TRY_TO(TraverseStmt(C->getHint()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDistScheduleClause(
+    OMPDistScheduleClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getChunkSize()));
+  return true;
+}
+
+template <typename Derived>
+bool
+RecursiveASTEnterExitVisitor<Derived>::VisitOMPDefaultmapClause(OMPDefaultmapClause *C) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPToClause(OMPToClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFromClause(OMPFromClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUseDevicePtrClause(
+    OMPUseDevicePtrClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUseDeviceAddrClause(
+    OMPUseDeviceAddrClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPIsDevicePtrClause(
+    OMPIsDevicePtrClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPHasDeviceAddrClause(
+    OMPHasDeviceAddrClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPNontemporalClause(
+    OMPNontemporalClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  for (auto *E : C->private_refs()) {
+    TRY_TO(TraverseStmt(E));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPOrderClause(OMPOrderClause *) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDetachClause(OMPDetachClause *C) {
+  TRY_TO(TraverseStmt(C->getEventHandler()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPUsesAllocatorsClause(
+    OMPUsesAllocatorsClause *C) {
+  for (unsigned I = 0, E = C->getNumberOfAllocators(); I < E; ++I) {
+    const OMPUsesAllocatorsClause::Data Data = C->getAllocatorData(I);
+    TRY_TO(TraverseStmt(Data.Allocator));
+    TRY_TO(TraverseStmt(Data.AllocatorTraits));
+  }
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPAffinityClause(
+    OMPAffinityClause *C) {
+  TRY_TO(TraverseStmt(C->getModifier()));
+  for (Expr *E : C->varlist())
+    TRY_TO(TraverseStmt(E));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPFilterClause(OMPFilterClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getThreadID()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPBindClause(OMPBindClause *C) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPXDynCGroupMemClause(
+    OMPXDynCGroupMemClause *C) {
+  TRY_TO(VisitOMPClauseWithPreInit(C));
+  TRY_TO(TraverseStmt(C->getSize()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPDoacrossClause(
+    OMPDoacrossClause *C) {
+  TRY_TO(VisitOMPClauseList(C));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPXAttributeClause(
+    OMPXAttributeClause *C) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOMPXBareClause(OMPXBareClause *C) {
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseOpenACCConstructStmt(
+    OpenACCConstructStmt *C) {
+  TRY_TO(VisitOpenACCClauseList(C->clauses()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::TraverseOpenACCAssociatedStmtConstruct(
+    OpenACCAssociatedStmtConstruct *S) {
+  TRY_TO(TraverseOpenACCConstructStmt(S));
+  TRY_TO(TraverseStmt(S->getAssociatedStmt()));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOpenACCClause(const OpenACCClause *C) {
+  for (const Stmt *Child : C->children())
+    TRY_TO(TraverseStmt(const_cast<Stmt *>(Child)));
+  return true;
+}
+
+template <typename Derived>
+bool RecursiveASTEnterExitVisitor<Derived>::VisitOpenACCClauseList(
+    ArrayRef<const OpenACCClause *> Clauses) {
+
+  for (const auto *C : Clauses)
+    TRY_TO(VisitOpenACCClause(C));
+  return true;
+}
+
+DEF_TRAVERSE_STMT(OpenACCComputeConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCLoopConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCCombinedConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCDataConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCEnterDataConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCExitDataConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCHostDataConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCWaitConstruct, {
+  if (S->hasDevNumExpr())
+    TRY_TO(TraverseStmt(S->getDevNumExpr()));
+  for (auto *E : S->getQueueIdExprs())
+    TRY_TO(TraverseStmt(E));
+  TRY_TO(VisitOpenACCClauseList(S->clauses()));
+})
+DEF_TRAVERSE_STMT(OpenACCInitConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCShutdownConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCSetConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCUpdateConstruct,
+                  { TRY_TO(VisitOpenACCClauseList(S->clauses())); })
+DEF_TRAVERSE_STMT(OpenACCAtomicConstruct,
+                  { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCCacheConstruct, {
+  for (auto *E : S->getVarList())
+    TRY_TO(TraverseStmt(E));
+})
+
+// Traverse HLSL: Out argument expression
+DEF_TRAVERSE_STMT(HLSLOutArgExpr, {})
+
+// FIXME: look at the following tricky-seeming exprs to see if we
+// need to recurse on anything.  These are ones that have methods
+// returning decls or qualtypes or nestednamespecifier -- though I'm
+// not sure if they own them -- or just seemed very complicated, or
+// had lots of sub-types to explore.
+//
+// VisitOverloadExpr and its children: recurse on template args? etc?
+
+// FIXME: go through all the stmts and exprs again, and see which of them
+// create new types, and recurse on the types (TypeLocs?) of those.
+// Candidates:
+//
+//    http://clang.llvm.org/doxygen/classclang_1_1CXXTypeidExpr.html
+//    http://clang.llvm.org/doxygen/classclang_1_1UnaryExprOrTypeTraitExpr.html
+//    http://clang.llvm.org/doxygen/classclang_1_1TypesCompatibleExpr.html
+//    Every class that has getQualifier.
+
+#undef DEF_TRAVERSE_STMT
+#undef TRAVERSE_STMT
+#undef TRAVERSE_STMT_BASE
+
+#undef TRY_TO
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 3edc8684d0a19..56976882be67b 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1273,8 +1273,11 @@ DEF_TRAVERSE_TYPELOC(RValueReferenceType,
 DEF_TRAVERSE_TYPELOC(MemberPointerType, {
   if (NestedNameSpecifierLoc QL = TL.getQualifierLoc())
     TRY_TO(TraverseNestedNameSpecifierLoc(QL));
+//  if (auto *TSI = TL.getClassTInfo())
+//    TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
   else
     TRY_TO(TraverseNestedNameSpecifier(TL.getTypePtr()->getQualifier()));
+//    TRY_TO(TraverseType(QualType(TL.getTypePtr()->getClass(), 0)));
   TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
 })
 
diff --git a/clang/include/clang/AST/StmtOpenACC.h b/clang/include/clang/AST/StmtOpenACC.h
index 39c4c81844911..f7b94259ee304 100644
--- a/clang/include/clang/AST/StmtOpenACC.h
+++ b/clang/include/clang/AST/StmtOpenACC.h
@@ -81,6 +81,7 @@ class OpenACCAssociatedStmtConstruct : public OpenACCConstructStmt {
   friend class ASTStmtWriter;
   friend class ASTStmtReader;
   template <typename Derived> friend class RecursiveASTVisitor;
+  template <typename Derived> friend class RecursiveASTEnterExitVisitor;
   Stmt *AssociatedStmt = nullptr;
 
 protected:
diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt
index 106c6b9dc38bd..80aac485ce500 100644
--- a/clang/unittests/Tooling/CMakeLists.txt
+++ b/clang/unittests/Tooling/CMakeLists.txt
@@ -49,6 +49,40 @@ add_clang_unittest(ToolingTests
   RecursiveASTVisitorTestDeclVisitor.cpp
   RecursiveASTVisitorTestPostOrderVisitor.cpp
   RecursiveASTVisitorTestTypeLocVisitor.cpp
+  RecursiveASTEnterExitVisitorTests/Attr.cpp
+  RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp
+  RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp
+  RecursiveASTEnterExitVisitorTests/CallbacksUnaryOperator.cpp
+  RecursiveASTEnterExitVisitorTests/CallbacksBinaryOperator.cpp
+  RecursiveASTEnterExitVisitorTests/CallbacksCompoundAssignOperator.cpp
+  RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp
+  RecursiveASTEnterExitVisitorTests/Class.cpp
+  RecursiveASTEnterExitVisitorTests/Concept.cpp
+  RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp
+  RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp
+  RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp
+  RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp
+  RecursiveASTEnterExitVisitorTests/CXXOperatorCallExprTraverser.cpp
+  RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp
+  RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp
+  RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp
+  RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp
+  RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp
+  RecursiveASTEnterExitVisitorTests/InitListExprPostOrderNoQueue.cpp
+  RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp
+  RecursiveASTEnterExitVisitorTests/InitListExprPreOrderNoQueue.cpp
+  RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp
+  RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp
+  RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp
+  RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp
+  RecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp
+  RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp
+  RecursiveASTEnterExitVisitorTests/ParenExpr.cpp
+  RecursiveASTEnterExitVisitorTests/TemplateArgumentLocTraverser.cpp
+  RecursiveASTEnterExitVisitorTests/TraversalScope.cpp
+  RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
+  RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
+  RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
   RefactoringActionRulesTest.cpp
   RefactoringCallbacksTest.cpp
   RefactoringTest.cpp
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
new file mode 100644
index 0000000000000..b087faee6256f
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
@@ -0,0 +1,138 @@
+//===- unittest/Tooling/EnterExitRecursiveASTVisitorTestDeclVisitor.cpp ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class VarDeclVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitVarDecl(VarDecl *Variable) override {
+    Match(Variable->getNameAsString(), Variable->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(EnterExitRecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
+  VarDeclVisitor Visitor;
+  Visitor.ExpectMatch("i", 2, 17);
+  EXPECT_TRUE(Visitor.runOver(
+    "int x[5];\n"
+    "void f() { for (int i : x) {} }",
+    VarDeclVisitor::Lang_CXX11));
+}
+
+class ParmVarDeclVisitorForImplicitCode : public ExpectedLocationVisitor {
+public:
+  ParmVarDeclVisitorForImplicitCode() { ShouldVisitImplicitCode = true; }
+
+  bool VisitParmVarDecl(ParmVarDecl *ParamVar) override {
+    Match(ParamVar->getNameAsString(), ParamVar->getBeginLoc());
+    return true;
+  }
+};
+
+// Test RAV visits parameter variable declaration of the implicit
+// copy assignment operator and implicit copy constructor.
+TEST(EnterExitRecursiveASTVisitor, VisitsParmVarDeclForImplicitCode) {
+  ParmVarDeclVisitorForImplicitCode Visitor;
+  // Match parameter variable name of implicit copy assignment operator and
+  // implicit copy constructor.
+  // This parameter name does not have a valid IdentifierInfo, and shares
+  // same SourceLocation with its class declaration, so we match an empty name
+  // with the class' source location.
+  Visitor.ExpectMatch("", 1, 7);
+  Visitor.ExpectMatch("", 3, 7);
+  EXPECT_TRUE(Visitor.runOver(
+    "class X {};\n"
+    "void foo(X a, X b) {a = b;}\n"
+    "class Y {};\n"
+    "void bar(Y a) {Y b = a;}"));
+}
+
+class NamedDeclVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitNamedDecl(NamedDecl *Decl) override {
+    std::string NameWithTemplateArgs;
+    llvm::raw_string_ostream OS(NameWithTemplateArgs);
+    Decl->getNameForDiagnostic(OS,
+                               Decl->getASTContext().getPrintingPolicy(),
+                               true);
+    Match(NameWithTemplateArgs, Decl->getLocation());
+    return true;
+  }
+};
+
+TEST(EnterExitRecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
+  // From cfe-commits/Week-of-Mon-20100830/033998.html
+  // Contrary to the approach suggested in that email, we visit all
+  // specializations when we visit the primary template.  Visiting them when we
+  // visit the associated specialization is problematic for specializations of
+  // template members of class templates.
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<bool>", 1, 26);
+  Visitor.ExpectMatch("A<char *>", 2, 26);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <class T> class A {};\n"
+    "template <class T> class A<T*> {};\n"
+    "A<bool> ab;\n"
+    "A<char*> acp;\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>", 1, 29);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A;\n"
+    "A<int> *p;\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A {\n"
+    "  template<typename U> struct B;\n"
+    "};\n"
+    "A<int>::B<char> *p;\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>", 1, 26);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> int A();\n"
+    "int k = A<int>();\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> struct A {\n"
+    "  template<typename U> static int B();\n"
+    "};\n"
+    "int k = A<int>::B<char>();\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, NoRecursionInSelfFriend) {
+  // From cfe-commits/Week-of-Mon-20100830/033977.html
+  NamedDeclVisitor Visitor;
+  Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename Container>\n"
+    "class vector_iterator {\n"
+    "    template <typename C> friend class vector_iterator;\n"
+    "};\n"
+    "vector_iterator<int> it_int;\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
new file mode 100644
index 0000000000000..5c97d0e475640
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
@@ -0,0 +1,114 @@
+//===- unittests/Tooling/EnterExitRecursiveASTVisitorPostOrderASTVisitor.cpp -------===//
+//
+// 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 contains tests for the post-order traversing functionality
+// of EnterExitRecursiveASTVisitor.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CRTPTestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+class RecordingVisitor : public CRTPTestVisitor<RecordingVisitor> {
+  bool VisitPostOrder;
+
+public:
+  explicit RecordingVisitor(bool VisitPostOrder)
+      : VisitPostOrder(VisitPostOrder) {}
+
+  // List of visited nodes during traversal.
+  std::vector<std::string> VisitedNodes;
+
+  bool shouldTraversePostOrder() const { return VisitPostOrder; }
+
+  bool VisitUnaryOperator(UnaryOperator *Op) {
+    VisitedNodes.push_back(std::string(Op->getOpcodeStr(Op->getOpcode())));
+    return true;
+  }
+
+  bool VisitBinaryOperator(BinaryOperator *Op) {
+    VisitedNodes.push_back(std::string(Op->getOpcodeStr()));
+    return true;
+  }
+
+  bool VisitIntegerLiteral(IntegerLiteral *Lit) {
+    VisitedNodes.push_back(toString(Lit->getValue(), 10, false));
+    return true;
+  }
+
+  bool VisitVarDecl(VarDecl *D) {
+    VisitedNodes.push_back(D->getNameAsString());
+    return true;
+  }
+
+  bool VisitCXXMethodDecl(CXXMethodDecl *D) {
+    VisitedNodes.push_back(D->getQualifiedNameAsString());
+    return true;
+  }
+
+  bool VisitReturnStmt(ReturnStmt *S) {
+    VisitedNodes.push_back("return");
+    return true;
+  }
+
+  bool VisitCXXRecordDecl(CXXRecordDecl *D) {
+    if (!D->isImplicit())
+      VisitedNodes.push_back(D->getQualifiedNameAsString());
+    return true;
+  }
+
+  bool VisitTemplateTypeParmType(TemplateTypeParmType *T) {
+    VisitedNodes.push_back(T->getDecl()->getQualifiedNameAsString());
+    return true;
+  }
+};
+} // namespace
+
+TEST(EnterExitRecursiveASTVisitor, PostOrderTraversal) {
+  // We traverse the translation unit and store all visited nodes.
+  RecordingVisitor Visitor(true);
+  Visitor.runOver("class A {\n"
+                  "  class B {\n"
+                  "    int foo() {\n"
+                  "      while(4) { int i = 9; int j = -5; }\n"
+                  "      return (1 + 3) + 2; }\n"
+                  "  };\n"
+                  "};\n");
+
+  std::vector<std::string> expected = {"4", "9",      "i",         "5",    "-",
+                                       "j", "1",      "3",         "+",    "2",
+                                       "+", "return", "A::B::foo", "A::B", "A"};
+  // Compare the list of actually visited nodes with the expected list of
+  // visited nodes.
+  ASSERT_EQ(expected.size(), Visitor.VisitedNodes.size());
+  for (std::size_t I = 0; I < expected.size(); I++) {
+    ASSERT_EQ(expected[I], Visitor.VisitedNodes[I]);
+  }
+}
+
+TEST(EnterExitRecursiveASTVisitor, NoPostOrderTraversal) {
+  // We traverse the translation unit and store all visited nodes.
+  RecordingVisitor Visitor(false);
+  Visitor.runOver("class A {\n"
+                  "  class B {\n"
+                  "    int foo() { return 1 + 2; }\n"
+                  "  };\n"
+                  "};\n");
+
+  std::vector<std::string> expected = {"A", "A::B", "A::B::foo", "return",
+                                       "+", "1",    "2"};
+  // Compare the list of actually visited nodes with the expected list of
+  // visited nodes.
+  ASSERT_EQ(expected.size(), Visitor.VisitedNodes.size());
+  for (std::size_t I = 0; I < expected.size(); I++) {
+    ASSERT_EQ(expected[I], Visitor.VisitedNodes[I]);
+  }
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
new file mode 100644
index 0000000000000..4da5288c4254b
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
@@ -0,0 +1,100 @@
+//===- unittest/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class TypeLocVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitTypeLoc(TypeLoc TypeLocation) override {
+    Match(TypeLocation.getType().getAsString(), TypeLocation.getBeginLoc());
+    return true;
+  }
+};
+
+TEST(EnterExitRecursiveASTVisitor, VisitsBaseClassDeclarations) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("class X", 1, 30);
+  EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("class X", 3, 18);
+  EXPECT_TRUE(Visitor.runOver(
+    "class Y;\n"
+    "class X {};\n"
+    "class Y : public X {};"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("class X", 2, 18);
+  EXPECT_TRUE(Visitor.runOver(
+    "class X {};\n"
+    "class Y : public X { class Z; };"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("X<Y>", 2, 18, 2);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> class X {};\n"
+    "class Y : public X<Y> {};"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("class X", 2, 23);
+  EXPECT_TRUE(Visitor.runOver(
+    "class X;\n"
+    "template<typename T = X> class Y;\n"
+    "template<typename T> class Y {};\n"));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsCompoundLiteralType) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("struct S", 1, 26);
+  EXPECT_TRUE(Visitor.runOver(
+      "int f() { return (struct S { int a; }){.a = 0}.a; }",
+      TypeLocVisitor::Lang_C));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsObjCPropertyType) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("NSNumber", 2, 33);
+  EXPECT_TRUE(Visitor.runOver(
+      "@class NSNumber; \n"
+      "@interface A @property (retain) NSNumber *x; @end\n",
+      TypeLocVisitor::Lang_OBJC));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitInvalidType) {
+  TypeLocVisitor Visitor;
+  // FIXME: It would be nice to have information about subtypes of invalid type
+  //Visitor.ExpectMatch("typeof(struct F *) []", 1, 1);
+  // Even if the full type is invalid, it should still find sub types
+  //Visitor.ExpectMatch("struct F", 1, 19);
+  EXPECT_FALSE(Visitor.runOver(
+      "__typeof__(struct F*) var[invalid];\n",
+      TypeLocVisitor::Lang_C));
+}
+
+TEST(EnterExitRecursiveASTVisitor, VisitsUsingEnumType) {
+  TypeLocVisitor Visitor;
+  Visitor.ExpectMatch("::A", 2, 12);
+  EXPECT_TRUE(Visitor.runOver("enum class A {}; \n"
+                              "using enum ::A;\n",
+                              TypeLocVisitor::Lang_CXX2a));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Attr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Attr.cpp
new file mode 100644
index 0000000000000..4886e00f3cc1d
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Attr.cpp
@@ -0,0 +1,51 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/Attr.cpp -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CRTPTestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Check to ensure that attributes and expressions within them are being
+// visited.
+class AttrVisitor : public CRTPExpectedLocationVisitor<AttrVisitor> {
+public:
+  bool VisitMemberExpr(MemberExpr *ME) {
+    Match(ME->getMemberDecl()->getNameAsString(), ME->getBeginLoc());
+    return true;
+  }
+  bool VisitAttr(Attr *A) {
+    Match("Attr", A->getLocation());
+    return true;
+  }
+  bool VisitGuardedByAttr(GuardedByAttr *A) {
+    Match("guarded_by", A->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, AttributesAreVisited) {
+  AttrVisitor Visitor;
+  Visitor.ExpectMatch("Attr", 4, 24);
+  Visitor.ExpectMatch("guarded_by", 4, 24);
+  Visitor.ExpectMatch("mu1",  4, 35);
+  Visitor.ExpectMatch("Attr", 5, 29);
+  Visitor.ExpectMatch("mu1",  5, 54);
+  Visitor.ExpectMatch("mu2",  5, 59);
+  EXPECT_TRUE(Visitor.runOver(
+    "class Foo {\n"
+    "  int mu1;\n"
+    "  int mu2;\n"
+    "  int a __attribute__((guarded_by(mu1)));\n"
+    "  void bar() __attribute__((exclusive_locks_required(mu1, mu2)));\n"
+    "};\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp
new file mode 100644
index 0000000000000..ca08f2233d8d6
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp
@@ -0,0 +1,34 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/BitfieldInitializer.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include <string>
+
+using namespace clang;
+
+namespace {
+
+// Check to ensure that bitfield initializers are visited.
+class BitfieldInitializerVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitIntegerLiteral(IntegerLiteral *IL) override {
+    Match(std::to_string(IL->getValue().getSExtValue()), IL->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, BitfieldInitializerIsVisited) {
+  BitfieldInitializerVisitor Visitor;
+  Visitor.ExpectMatch("123", 2, 15);
+  EXPECT_TRUE(Visitor.runOver("struct S {\n"
+                              "  int x : 8 = 123;\n"
+                              "};\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp
new file mode 100644
index 0000000000000..39070daed9c00
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp
@@ -0,0 +1,36 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/CXXBoolLiteralExpr.cpp ---===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class CXXBoolLiteralExprVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) override {
+    if (BE->getValue())
+      Match("true", BE->getLocation());
+    else
+      Match("false", BE->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) {
+  CXXBoolLiteralExprVisitor Visitor;
+  Visitor.ExpectMatch("true", 2, 19);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<bool B> class X;\n"
+    "template<bool B = true> class Y;\n"
+    "template<bool B> class Y {};\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp
new file mode 100644
index 0000000000000..d4a050d7663f6
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp
@@ -0,0 +1,97 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/CXXMemberCall.cpp --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class CXXMemberCallVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitCXXMemberCallExpr(CXXMemberCallExpr *Call) override {
+    Match(Call->getMethodDecl()->getQualifiedNameAsString(),
+          Call->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCallInTemplateInstantiation) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("Y::x", 3, 3);
+  EXPECT_TRUE(Visitor.runOver(
+    "struct Y { void x(); };\n"
+    "template<typename T> void y(T t) {\n"
+    "  t.x();\n"
+    "}\n"
+    "void foo() { y<Y>(Y()); }"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCallInNestedFunctionTemplateInstantiation) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("Y::x", 4, 5);
+  EXPECT_TRUE(Visitor.runOver(
+    "struct Y { void x(); };\n"
+    "template<typename T> struct Z {\n"
+    "  template<typename U> static void f() {\n"
+    "    T().x();\n"
+    "  }\n"
+    "};\n"
+    "void foo() { Z<Y>::f<int>(); }"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCallInNestedClassTemplateInstantiation) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("A::x", 5, 7);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <typename T1> struct X {\n"
+    "  template <typename T2> struct Y {\n"
+    "    void f() {\n"
+    "      T2 y;\n"
+    "      y.x();\n"
+    "    }\n"
+    "  };\n"
+    "};\n"
+    "struct A { void x(); };\n"
+    "int main() {\n"
+    "  (new X<A>::Y<A>())->f();\n"
+    "}"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCallInPartialTemplateSpecialization) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("A::x", 6, 20);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <typename T1> struct X {\n"
+    "  template <typename T2, bool B> struct Y { void g(); };\n"
+    "};\n"
+    "template <typename T1> template <typename T2>\n"
+    "struct X<T1>::Y<T2, true> {\n"
+    "  void f() { T2 y; y.x(); }\n"
+    "};\n"
+    "struct A { void x(); };\n"
+    "int main() {\n"
+    "  (new X<A>::Y<A, true>())->f();\n"
+    "}\n"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsExplicitTemplateSpecialization) {
+  CXXMemberCallVisitor Visitor;
+  Visitor.ExpectMatch("A::f", 4, 5);
+  EXPECT_TRUE(Visitor.runOver(
+    "struct A {\n"
+    "  void f() const {}\n"
+    "  template<class T> void g(const T& t) const {\n"
+    "    t.f();\n"
+    "  }\n"
+    "};\n"
+    "template void A::g(const A& a) const;\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp
new file mode 100644
index 0000000000000..8d495c4526f02
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp
@@ -0,0 +1,73 @@
+//=------ unittest/Tooling/RecursiveASTEnterExitVisitorTests/CXXMethodDecl.cpp ------=//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include "clang/AST/Expr.h"
+
+using namespace clang;
+
+namespace {
+
+class CXXMethodDeclVisitor : public ExpectedLocationVisitor {
+public:
+  CXXMethodDeclVisitor(bool VisitImplicitCode) {
+    ShouldVisitImplicitCode = VisitImplicitCode;
+  }
+
+  bool VisitDeclRefExpr(DeclRefExpr *D) override {
+    Match("declref", D->getLocation());
+    return true;
+  }
+
+  bool VisitParmVarDecl(ParmVarDecl *P) override {
+    Match("parm", P->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, CXXMethodDeclNoDefaultBodyVisited) {
+  for (bool VisitImplCode : {false, true}) {
+    CXXMethodDeclVisitor Visitor(VisitImplCode);
+    if (VisitImplCode)
+      Visitor.ExpectMatch("declref", 8, 28);
+    else
+      Visitor.DisallowMatch("declref", 8, 28);
+
+    Visitor.ExpectMatch("parm", 8, 27);
+    llvm::StringRef Code = R"cpp(
+      struct B {};
+      struct A {
+        B BB;
+        A &operator=(A &&O);
+      };
+
+      A &A::operator=(A &&O) = default;
+    )cpp";
+    EXPECT_TRUE(Visitor.runOver(Code, CXXMethodDeclVisitor::Lang_CXX11));
+  }
+}
+
+TEST(RecursiveASTEnterExitVisitor, FunctionDeclNoDefaultBodyVisited) {
+  for (bool VisitImplCode : {false, true}) {
+    CXXMethodDeclVisitor Visitor(VisitImplCode);
+    if (VisitImplCode)
+      Visitor.ExpectMatch("declref", 4, 58, /*Times=*/2);
+    else
+      Visitor.DisallowMatch("declref", 4, 58);
+    llvm::StringRef Code = R"cpp(
+      struct s {
+        int x;
+        friend auto operator==(s a, s b) -> bool = default;
+      };
+      bool k = s() == s(); // make sure clang generates the "==" definition.
+    )cpp";
+    EXPECT_TRUE(Visitor.runOver(Code, CXXMethodDeclVisitor::Lang_CXX2a));
+  }
+}
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXOperatorCallExprTraverser.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXOperatorCallExprTraverser.cpp
new file mode 100644
index 0000000000000..76eaa61e99994
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CXXOperatorCallExprTraverser.cpp
@@ -0,0 +1,36 @@
+//===- CXXOperatorCallExprTraverser.cpp -----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class CXXOperatorCallExprTraverser : public ExpectedLocationVisitor {
+public:
+  // Use Traverse, not Visit, to check that data recursion optimization isn't
+  // bypassing the call of this function.
+  bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *CE) override {
+    Match(getOperatorSpelling(CE->getOperator()), CE->getExprLoc());
+    return ExpectedLocationVisitor::TraverseCXXOperatorCallExpr(CE);
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, TraversesOverloadedOperator) {
+  CXXOperatorCallExprTraverser Visitor;
+  Visitor.ExpectMatch("()", 4, 9);
+  EXPECT_TRUE(Visitor.runOver(
+    "struct A {\n"
+    "  int operator()();\n"
+    "} a;\n"
+    "int k = a();\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksBinaryOperator.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksBinaryOperator.cpp
new file mode 100644
index 0000000000000..b881370b2c3e8
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksBinaryOperator.cpp
@@ -0,0 +1,211 @@
+//===- CallbacksBinaryOperator.cpp ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CallbacksCommon.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::TraverseBinaryOperator(BO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinaryOperator BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinaryOperator BinaryOperator(+)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(3)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor,
+     StmtCallbacks_TraverseBinaryOperator_WalkUpFromBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::TraverseBinaryOperator(BO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinaryOperator BinaryOperator(+)
+  WalkUpFromBinaryOperator BinaryOperator(+)
+    WalkUpFromExpr BinaryOperator(+)
+      WalkUpFromStmt BinaryOperator(+)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+  WalkUpFromBinaryOperator BinaryOperator(+)
+    WalkUpFromExpr BinaryOperator(+)
+      WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_WalkUpFromBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp
new file mode 100644
index 0000000000000..5561971af2930
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp
@@ -0,0 +1,249 @@
+//===-- unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCallExpr.cpp --===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CallbacksCommon.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseCallExpr) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCallExpr(CallExpr *CE) {
+      recordCallback(__func__, CE,
+                     [&]() { RecordingVisitorBase::TraverseCallExpr(CE); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+TraverseCallExpr CallExpr(add)
+  WalkUpFromStmt CallExpr(add)
+  WalkUpFromStmt ImplicitCastExpr
+  WalkUpFromStmt DeclRefExpr(add)
+  WalkUpFromStmt IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt BinaryOperator(+)
+TraverseCallExpr CallExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+  WalkUpFromStmt ImplicitCastExpr
+  WalkUpFromStmt IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(5)
+  WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseCallExpr_WalkUpFromCallExpr) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCallExpr(CallExpr *CE) {
+      recordCallback(__func__, CE,
+                     [&]() { RecordingVisitorBase::TraverseCallExpr(CE); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCallExpr(CallExpr *CE) {
+      recordCallback(__func__, CE,
+                     [&]() { RecordingVisitorBase::WalkUpFromCallExpr(CE); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+TraverseCallExpr CallExpr(add)
+  WalkUpFromCallExpr CallExpr(add)
+    WalkUpFromExpr CallExpr(add)
+      WalkUpFromStmt CallExpr(add)
+  WalkUpFromExpr ImplicitCastExpr
+    WalkUpFromStmt ImplicitCastExpr
+  WalkUpFromExpr DeclRefExpr(add)
+    WalkUpFromStmt DeclRefExpr(add)
+  WalkUpFromExpr IntegerLiteral(4)
+    WalkUpFromStmt IntegerLiteral(4)
+  WalkUpFromExpr IntegerLiteral(5)
+    WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+TraverseCallExpr CallExpr(add)
+  WalkUpFromExpr DeclRefExpr(add)
+    WalkUpFromStmt DeclRefExpr(add)
+  WalkUpFromExpr ImplicitCastExpr
+    WalkUpFromStmt ImplicitCastExpr
+  WalkUpFromExpr IntegerLiteral(4)
+    WalkUpFromStmt IntegerLiteral(4)
+  WalkUpFromExpr IntegerLiteral(5)
+    WalkUpFromStmt IntegerLiteral(5)
+  WalkUpFromCallExpr CallExpr(add)
+    WalkUpFromExpr CallExpr(add)
+      WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_WalkUpFromCallExpr) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCallExpr(CallExpr *CE) {
+      recordCallback(__func__, CE,
+                     [&]() { RecordingVisitorBase::WalkUpFromCallExpr(CE); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromCallExpr CallExpr(add)
+  WalkUpFromExpr CallExpr(add)
+    WalkUpFromStmt CallExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromExpr IntegerLiteral(5)
+  WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromExpr IntegerLiteral(5)
+  WalkUpFromStmt IntegerLiteral(5)
+WalkUpFromCallExpr CallExpr(add)
+  WalkUpFromExpr CallExpr(add)
+    WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCommon.h b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCommon.h
new file mode 100644
index 0000000000000..6e940e63d5d11
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCommon.h
@@ -0,0 +1,102 @@
+//===--- unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCommon.h -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CRTPTestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+enum class ShouldTraversePostOrder : bool {
+  No = false,
+  Yes = true,
+};
+
+/// Base class for tests for RecursiveASTEnterExitVisitor tests that validate the
+/// sequence of calls to user-defined callbacks like Traverse*(), WalkUp*(),
+/// Visit*().
+template <typename Derived>
+class RecordingVisitorBase : public CRTPTestVisitor<Derived> {
+  ShouldTraversePostOrder ShouldTraversePostOrderValue;
+
+public:
+  RecordingVisitorBase(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+      : ShouldTraversePostOrderValue(ShouldTraversePostOrderValue) {}
+
+  bool shouldTraversePostOrder() const {
+    return static_cast<bool>(ShouldTraversePostOrderValue);
+  }
+
+  // Callbacks received during traversal.
+  std::string CallbackLog;
+  unsigned CallbackLogIndent = 0;
+
+  std::string stmtToString(Stmt *S) {
+    StringRef ClassName = S->getStmtClassName();
+    if (IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S)) {
+      return (ClassName + "(" + toString(IL->getValue(), 10, false) + ")").str();
+    }
+    if (UnaryOperator *UO = dyn_cast<UnaryOperator>(S)) {
+      return (ClassName + "(" + UnaryOperator::getOpcodeStr(UO->getOpcode()) +
+              ")")
+          .str();
+    }
+    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
+      return (ClassName + "(" + BinaryOperator::getOpcodeStr(BO->getOpcode()) +
+              ")")
+          .str();
+    }
+    if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
+      if (FunctionDecl *Callee = CE->getDirectCallee()) {
+        if (Callee->getIdentifier()) {
+          return (ClassName + "(" + Callee->getName() + ")").str();
+        }
+      }
+    }
+    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) {
+      if (NamedDecl *ND = DRE->getFoundDecl()) {
+        if (ND->getIdentifier()) {
+          return (ClassName + "(" + ND->getName() + ")").str();
+        }
+      }
+    }
+    return ClassName.str();
+  }
+
+  /// Record the fact that the user-defined callback member function
+  /// \p CallbackName was called with the argument \p S. Then, record the
+  /// effects of calling the default implementation \p CallDefaultFn.
+  template <typename CallDefault>
+  void recordCallback(StringRef CallbackName, Stmt *S,
+                      CallDefault CallDefaultFn) {
+    for (unsigned i = 0; i != CallbackLogIndent; ++i) {
+      CallbackLog += "  ";
+    }
+    CallbackLog += (CallbackName + " " + stmtToString(S) + "\n").str();
+    ++CallbackLogIndent;
+    CallDefaultFn();
+    --CallbackLogIndent;
+  }
+};
+
+template <typename VisitorTy>
+::testing::AssertionResult visitorCallbackLogEqual(VisitorTy Visitor,
+                                                   StringRef Code,
+                                                   StringRef ExpectedLog) {
+  Visitor.runOver(Code);
+  // EXPECT_EQ shows the diff between the two strings if they are different.
+  EXPECT_EQ(ExpectedLog.trim().str(),
+            StringRef(Visitor.CallbackLog).trim().str());
+  if (ExpectedLog.trim() != StringRef(Visitor.CallbackLog).trim()) {
+    return ::testing::AssertionFailure();
+  }
+  return ::testing::AssertionSuccess();
+}
+
+} // namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCompoundAssignOperator.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCompoundAssignOperator.cpp
new file mode 100644
index 0000000000000..f2e1be785421b
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksCompoundAssignOperator.cpp
@@ -0,0 +1,212 @@
+//===- CallbacksCompoundAssignOperator.cpp --------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CallbacksCommon.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromStmt CompoundAssignOperator(+=)
+  WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(
+    RecursiveASTEnterExitVisitor,
+    StmtCallbacks_TraverseCompoundAssignOperator_WalkUpFromCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+    WalkUpFromExpr CompoundAssignOperator(+=)
+      WalkUpFromStmt CompoundAssignOperator(+=)
+  WalkUpFromExpr DeclRefExpr(a)
+    WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr DeclRefExpr(a)
+    WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+    WalkUpFromExpr CompoundAssignOperator(+=)
+      WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_WalkUpFromCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp
new file mode 100644
index 0000000000000..e37aae4c70880
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp
@@ -0,0 +1,285 @@
+//===--- unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksLeaf.cpp -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CallbacksCommon.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseLeaf) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseIntegerLiteral(IntegerLiteral *IL) {
+      recordCallback(__func__, IL, [&]() {
+        RecordingVisitorBase::TraverseIntegerLiteral(IL);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+TraverseIntegerLiteral IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt BinaryOperator(+)
+TraverseIntegerLiteral IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+TraverseIntegerLiteral IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt ImplicitCastExpr
+WalkUpFromStmt DeclRefExpr(add)
+TraverseIntegerLiteral IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+TraverseIntegerLiteral IntegerLiteral(5)
+  WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+TraverseIntegerLiteral IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseIntegerLiteral IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+TraverseIntegerLiteral IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromStmt ImplicitCastExpr
+TraverseIntegerLiteral IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+TraverseIntegerLiteral IntegerLiteral(5)
+  WalkUpFromStmt IntegerLiteral(5)
+WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseLeaf_WalkUpFromLeaf) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseIntegerLiteral(IntegerLiteral *IL) {
+      recordCallback(__func__, IL, [&]() {
+        RecordingVisitorBase::TraverseIntegerLiteral(IL);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromIntegerLiteral(IntegerLiteral *IL) {
+      recordCallback(__func__, IL, [&]() {
+        RecordingVisitorBase::WalkUpFromIntegerLiteral(IL);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+TraverseIntegerLiteral IntegerLiteral(1)
+  WalkUpFromIntegerLiteral IntegerLiteral(1)
+    WalkUpFromExpr IntegerLiteral(1)
+      WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+TraverseIntegerLiteral IntegerLiteral(2)
+  WalkUpFromIntegerLiteral IntegerLiteral(2)
+    WalkUpFromExpr IntegerLiteral(2)
+      WalkUpFromStmt IntegerLiteral(2)
+TraverseIntegerLiteral IntegerLiteral(3)
+  WalkUpFromIntegerLiteral IntegerLiteral(3)
+    WalkUpFromExpr IntegerLiteral(3)
+      WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr CallExpr(add)
+  WalkUpFromStmt CallExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+TraverseIntegerLiteral IntegerLiteral(4)
+  WalkUpFromIntegerLiteral IntegerLiteral(4)
+    WalkUpFromExpr IntegerLiteral(4)
+      WalkUpFromStmt IntegerLiteral(4)
+TraverseIntegerLiteral IntegerLiteral(5)
+  WalkUpFromIntegerLiteral IntegerLiteral(5)
+    WalkUpFromExpr IntegerLiteral(5)
+      WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+TraverseIntegerLiteral IntegerLiteral(1)
+  WalkUpFromIntegerLiteral IntegerLiteral(1)
+    WalkUpFromExpr IntegerLiteral(1)
+      WalkUpFromStmt IntegerLiteral(1)
+TraverseIntegerLiteral IntegerLiteral(2)
+  WalkUpFromIntegerLiteral IntegerLiteral(2)
+    WalkUpFromExpr IntegerLiteral(2)
+      WalkUpFromStmt IntegerLiteral(2)
+TraverseIntegerLiteral IntegerLiteral(3)
+  WalkUpFromIntegerLiteral IntegerLiteral(3)
+    WalkUpFromExpr IntegerLiteral(3)
+      WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+TraverseIntegerLiteral IntegerLiteral(4)
+  WalkUpFromIntegerLiteral IntegerLiteral(4)
+    WalkUpFromExpr IntegerLiteral(4)
+      WalkUpFromStmt IntegerLiteral(4)
+TraverseIntegerLiteral IntegerLiteral(5)
+  WalkUpFromIntegerLiteral IntegerLiteral(5)
+    WalkUpFromExpr IntegerLiteral(5)
+      WalkUpFromStmt IntegerLiteral(5)
+WalkUpFromExpr CallExpr(add)
+  WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_WalkUpFromLeaf) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromIntegerLiteral(IntegerLiteral *IL) {
+      recordCallback(__func__, IL, [&]() {
+        RecordingVisitorBase::WalkUpFromIntegerLiteral(IL);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void add(int, int);
+void test() {
+  1;
+  2 + 3;
+  add(4, 5);
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromIntegerLiteral IntegerLiteral(1)
+  WalkUpFromExpr IntegerLiteral(1)
+    WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromIntegerLiteral IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromIntegerLiteral IntegerLiteral(3)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr CallExpr(add)
+  WalkUpFromStmt CallExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromIntegerLiteral IntegerLiteral(4)
+  WalkUpFromExpr IntegerLiteral(4)
+    WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromIntegerLiteral IntegerLiteral(5)
+  WalkUpFromExpr IntegerLiteral(5)
+    WalkUpFromStmt IntegerLiteral(5)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromIntegerLiteral IntegerLiteral(1)
+  WalkUpFromExpr IntegerLiteral(1)
+    WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromIntegerLiteral IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromIntegerLiteral IntegerLiteral(3)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr DeclRefExpr(add)
+  WalkUpFromStmt DeclRefExpr(add)
+WalkUpFromExpr ImplicitCastExpr
+  WalkUpFromStmt ImplicitCastExpr
+WalkUpFromIntegerLiteral IntegerLiteral(4)
+  WalkUpFromExpr IntegerLiteral(4)
+    WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromIntegerLiteral IntegerLiteral(5)
+  WalkUpFromExpr IntegerLiteral(5)
+    WalkUpFromStmt IntegerLiteral(5)
+WalkUpFromExpr CallExpr(add)
+  WalkUpFromStmt CallExpr(add)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksUnaryOperator.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksUnaryOperator.cpp
new file mode 100644
index 0000000000000..e9ae3fceca28d
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/CallbacksUnaryOperator.cpp
@@ -0,0 +1,201 @@
+//===- CallbacksUnaryOperator.cpp -----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CallbacksCommon.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_TraverseUnaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::TraverseUnaryOperator(UO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryOperator UnaryOperator(-)
+  WalkUpFromStmt UnaryOperator(-)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryOperator UnaryOperator(-)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt UnaryOperator(-)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor,
+     StmtCallbacks_TraverseUnaryOperator_WalkUpFromUnaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::TraverseUnaryOperator(UO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::WalkUpFromUnaryOperator(UO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryOperator UnaryOperator(-)
+  WalkUpFromUnaryOperator UnaryOperator(-)
+    WalkUpFromExpr UnaryOperator(-)
+      WalkUpFromStmt UnaryOperator(-)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromUnaryOperator UnaryOperator(-)
+    WalkUpFromExpr UnaryOperator(-)
+      WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, StmtCallbacks_WalkUpFromUnaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::WalkUpFromUnaryOperator(UO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Class.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Class.cpp
new file mode 100644
index 0000000000000..a62eff350889e
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Class.cpp
@@ -0,0 +1,42 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/Class.cpp ----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Checks for lambda classes that are not marked as implicitly-generated.
+// (There should be none.)
+class ClassVisitor : public ExpectedLocationVisitor {
+public:
+  ClassVisitor() : SawNonImplicitLambdaClass(false) {}
+
+  bool VisitCXXRecordDecl(CXXRecordDecl *record) override {
+    if (record->isLambda() && !record->isImplicit())
+      SawNonImplicitLambdaClass = true;
+    return true;
+  }
+
+  bool sawOnlyImplicitLambdaClasses() const {
+    return !SawNonImplicitLambdaClass;
+  }
+
+private:
+  bool SawNonImplicitLambdaClass;
+};
+
+TEST(RecursiveASTEnterExitVisitor, LambdaClosureTypesAreImplicit) {
+  ClassVisitor Visitor;
+  EXPECT_TRUE(Visitor.runOver("auto lambda = []{};", ClassVisitor::Lang_CXX11));
+  EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Concept.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Concept.cpp
new file mode 100644
index 0000000000000..03b932ddc6d3a
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/Concept.cpp
@@ -0,0 +1,167 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/Concept.cpp----------------==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include "clang/AST/ASTConcept.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/Type.h"
+
+using namespace clang;
+
+namespace {
+
+struct ConceptVisitor : ExpectedLocationVisitor {
+  ConceptVisitor(bool VisitImplicitCode = false) {
+    ShouldVisitImplicitCode = VisitImplicitCode;
+  }
+
+  bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) override {
+    ++ConceptSpecializationExprsVisited;
+    return true;
+  }
+  bool TraverseTypeConstraint(const TypeConstraint *C) override {
+    ++TypeConstraintsTraversed;
+    return ExpectedLocationVisitor::TraverseTypeConstraint(C);
+  }
+  bool TraverseConceptRequirement(concepts::Requirement *R) override {
+    ++ConceptRequirementsTraversed;
+    return ExpectedLocationVisitor::TraverseConceptRequirement(R);
+  }
+  bool TraverseConceptReference(ConceptReference *CR) override {
+    ++ConceptReferencesTraversed;
+    return ExpectedLocationVisitor::TraverseConceptReference(CR);
+  }
+  bool VisitConceptReference(ConceptReference *CR) override {
+    ++ConceptReferencesVisited;
+    return true;
+  }
+
+  int ConceptSpecializationExprsVisited = 0;
+  int TypeConstraintsTraversed = 0;
+  int ConceptRequirementsTraversed = 0;
+  int ConceptReferencesTraversed = 0;
+  int ConceptReferencesVisited = 0;
+};
+
+TEST(RecursiveASTEnterExitVisitor, Concepts) {
+  {
+    ConceptVisitor Visitor{true};
+    EXPECT_TRUE(
+        Visitor.runOver("template <typename T> concept Fooable = true;\n"
+                        "template <Fooable T> void bar(T);",
+                        ConceptVisitor::Lang_CXX2a));
+    // Check that we traverse the "Fooable T" template parameter's
+    // TypeConstraint's ImmediatelyDeclaredConstraint, which is a
+    // ConceptSpecializationExpr.
+    EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited);
+    // Also check we traversed the TypeConstraint that produced the expr.
+    EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
+  }
+
+  {
+    ConceptVisitor Visitor; // Don't visit implicit code now.
+    EXPECT_TRUE(
+        Visitor.runOver("template <typename T> concept Fooable = true;\n"
+                        "template <Fooable T> void bar(T);",
+                        ConceptVisitor::Lang_CXX2a));
+    // Check that we only visit the TypeConstraint, but not the implicitly
+    // generated immediately declared expression.
+    EXPECT_EQ(0, Visitor.ConceptSpecializationExprsVisited);
+    EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
+  }
+
+  {
+    ConceptVisitor Visitor;
+    EXPECT_TRUE(
+        Visitor.runOver("template <class T> concept A = true;\n"
+                        "template <class T> struct vector {};\n"
+                        "template <class T> concept B = requires(T x) {\n"
+                        "  typename vector<T*>;\n"
+                        "  {x} -> A;\n"
+                        "  requires true;\n"
+                        "};",
+                        ConceptVisitor::Lang_CXX2a));
+    EXPECT_EQ(3, Visitor.ConceptRequirementsTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
+    EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
+  }
+
+  ConceptVisitor Visitor;
+  llvm::StringRef Code =
+      R"cpp(
+template<typename T> concept True = false;
+template <typename F> struct Foo {};
+
+template <typename F>
+  requires requires { requires True<F>; }
+struct Foo<F> {};
+
+template <typename F> requires True<F>
+struct Foo<F>  {};
+  )cpp";
+  EXPECT_TRUE(Visitor.runOver(Code, ConceptVisitor::Lang_CXX2a));
+  // Check that the concept references from the partial specializations are
+  // visited.
+  EXPECT_EQ(2, Visitor.ConceptReferencesTraversed);
+  EXPECT_EQ(2, Visitor.ConceptReferencesVisited);
+}
+
+struct VisitDeclOnlyOnce : ExpectedLocationVisitor {
+  VisitDeclOnlyOnce() { ShouldWalkTypesOfTypeLocs = false; }
+
+  bool VisitConceptDecl(ConceptDecl *D) override {
+    ++ConceptDeclsVisited;
+    return true;
+  }
+
+  bool VisitAutoType(AutoType *) override {
+    ++AutoTypeVisited;
+    return true;
+  }
+  bool VisitAutoTypeLoc(AutoTypeLoc) override {
+    ++AutoTypeLocVisited;
+    return true;
+  }
+  bool VisitConceptReference(ConceptReference *) override {
+    ++ConceptReferencesVisited;
+    return true;
+  }
+
+  bool TraverseVarDecl(VarDecl *V) override {
+    // The base traversal visits only the `TypeLoc`.
+    // However, in the test we also validate the underlying `QualType`.
+    TraverseType(V->getType());
+    return ExpectedLocationVisitor::TraverseVarDecl(V);
+  }
+
+  int ConceptDeclsVisited = 0;
+  int AutoTypeVisited = 0;
+  int AutoTypeLocVisited = 0;
+  int ConceptReferencesVisited = 0;
+};
+
+TEST(RecursiveASTEnterExitVisitor, ConceptDeclInAutoType) {
+  // Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the
+  // underlying concept.
+  VisitDeclOnlyOnce Visitor;
+  Visitor.runOver("template <class T> concept A = true;\n"
+                  "A auto i = 0;\n",
+                  VisitDeclOnlyOnce::Lang_CXX2a);
+  EXPECT_EQ(1, Visitor.AutoTypeVisited);
+  EXPECT_EQ(1, Visitor.AutoTypeLocVisited);
+  EXPECT_EQ(1, Visitor.ConceptDeclsVisited);
+  EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp
new file mode 100644
index 0000000000000..fddda5a11f48d
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp
@@ -0,0 +1,66 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/ConstructExpr.cpp --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+/// \brief A visitor that optionally includes implicit code and matches
+/// CXXConstructExpr.
+///
+/// The name recorded for the match is the name of the class whose constructor
+/// is invoked by the CXXConstructExpr, not the name of the class whose
+/// constructor the CXXConstructExpr is contained in.
+class ConstructExprVisitor : public ExpectedLocationVisitor {
+public:
+  ConstructExprVisitor() { ShouldVisitImplicitCode = false; }
+
+  bool VisitCXXConstructExpr(CXXConstructExpr *Expr) override {
+    if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) {
+      if (const CXXRecordDecl* Class = Ctor->getParent()) {
+        Match(Class->getName(), Expr->getLocation());
+      }
+    }
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, CanVisitImplicitMemberInitializations) {
+  ConstructExprVisitor Visitor;
+  Visitor.ShouldVisitImplicitCode = true;
+  Visitor.ExpectMatch("WithCtor", 2, 8);
+  // Simple has a constructor that implicitly initializes 'w'.  Test
+  // that a visitor that visits implicit code visits that initialization.
+  // Note: Clang lazily instantiates implicit declarations, so we need
+  // to use them in order to force them to appear in the AST.
+  EXPECT_TRUE(Visitor.runOver(
+      "struct WithCtor { WithCtor(); }; \n"
+      "struct Simple { WithCtor w; }; \n"
+      "int main() { Simple s; }\n"));
+}
+
+// The same as CanVisitImplicitMemberInitializations, but checking that the
+// visits are omitted when the visitor does not include implicit code.
+TEST(RecursiveASTEnterExitVisitor, CanSkipImplicitMemberInitializations) {
+  ConstructExprVisitor Visitor;
+  Visitor.ShouldVisitImplicitCode = false;
+  Visitor.DisallowMatch("WithCtor", 2, 8);
+  // Simple has a constructor that implicitly initializes 'w'.  Test
+  // that a visitor that skips implicit code skips that initialization.
+  // Note: Clang lazily instantiates implicit declarations, so we need
+  // to use them in order to force them to appear in the AST.
+  EXPECT_TRUE(Visitor.runOver(
+      "struct WithCtor { WithCtor(); }; \n"
+      "struct Simple { WithCtor w; }; \n"
+      "int main() { Simple s; }\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp
new file mode 100644
index 0000000000000..724ff6c2e6d7b
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp
@@ -0,0 +1,116 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/DeclRefExpr.cpp ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class DeclRefExprVisitor : public ExpectedLocationVisitor {
+public:
+  DeclRefExprVisitor() { ShouldVisitImplicitCode = false; }
+
+  bool VisitDeclRefExpr(DeclRefExpr *Reference) override {
+    Match(Reference->getNameInfo().getAsString(), Reference->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsBaseClassTemplateArguments) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("x", 2, 3);
+  EXPECT_TRUE(Visitor.runOver(
+    "void x(); template <void (*T)()> class X {};\nX<x> y;"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCXXForRangeStmtRange) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("x", 2, 25);
+  Visitor.ExpectMatch("x", 2, 30);
+  EXPECT_TRUE(Visitor.runOver(
+    "int x[5];\n"
+    "void f() { for (int i : x) { x[0] = 1; } }",
+    DeclRefExprVisitor::Lang_CXX11));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCallExpr) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("x", 1, 22);
+  EXPECT_TRUE(Visitor.runOver(
+    "void x(); void y() { x(); }"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsExplicitLambdaCaptureInit) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("i", 1, 20);
+  EXPECT_TRUE(Visitor.runOver(
+    "void f() { int i; [i]{}; }",
+    DeclRefExprVisitor::Lang_CXX11));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsUseOfImplicitLambdaCapture) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("i", 1, 24);
+  EXPECT_TRUE(Visitor.runOver(
+    "void f() { int i; [=]{ i; }; }",
+    DeclRefExprVisitor::Lang_CXX11));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsImplicitLambdaCaptureInit) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ShouldVisitImplicitCode = true;
+  // We're expecting "i" to be visited twice: once for the initialization expr
+  // for the captured variable "i" outside of the lambda body, and again for
+  // the use of "i" inside the lambda.
+  Visitor.ExpectMatch("i", 1, 20, /*Times=*/1);
+  Visitor.ExpectMatch("i", 1, 24, /*Times=*/1);
+  EXPECT_TRUE(Visitor.runOver(
+    "void f() { int i; [=]{ i; }; }",
+    DeclRefExprVisitor::Lang_CXX11));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsLambdaInitCaptureInit) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("i", 1, 24);
+  EXPECT_TRUE(Visitor.runOver(
+    "void f() { int i; [a = i + 1]{}; }",
+    DeclRefExprVisitor::Lang_CXX14));
+}
+
+/* FIXME: According to Richard Smith this is a bug in the AST.
+TEST(RecursiveASTEnterExitVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("x", 3, 43);
+  EXPECT_TRUE(Visitor.runOver(
+    "template <typename T> void x();\n"
+    "template <void (*T)()> class X {};\n"
+    "template <typename T> class Y : public X< x<T> > {};\n"
+    "Y<int> y;"));
+}
+*/
+
+TEST(RecursiveASTEnterExitVisitor, VisitsExtension) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("s", 1, 24);
+  EXPECT_TRUE(Visitor.runOver(
+    "int s = __extension__ (s);\n"));
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsCopyExprOfBlockDeclCapture) {
+  DeclRefExprVisitor Visitor;
+  Visitor.ExpectMatch("x", 3, 24);
+  EXPECT_TRUE(Visitor.runOver("void f(int(^)(int)); \n"
+                              "void g() { \n"
+                              "  f([&](int x){ return x; }); \n"
+                              "}",
+                              DeclRefExprVisitor::Lang_OBJCXX11));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp
new file mode 100644
index 0000000000000..6ecea2a30d5bf
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp
@@ -0,0 +1,83 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/DeductionGuide.cpp -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include <string>
+
+using namespace clang;
+
+namespace {
+
+class DeductionGuideVisitor : public ExpectedLocationVisitor {
+public:
+  DeductionGuideVisitor(bool VisitImplicitCode) {
+    ShouldVisitImplicitCode = VisitImplicitCode;
+    ShouldVisitTemplateInstantiations = false;
+  }
+
+  bool VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) override {
+    std::string Storage;
+    llvm::raw_string_ostream Stream(Storage);
+    D->print(Stream);
+    Match(Storage, D->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, DeductionGuideNonImplicitMode) {
+  DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ false);
+  // Verify that the synthezied deduction guide for alias is not visited in
+  // RAV's implicit mode.
+  Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+  Visitor.DisallowMatch("Bar(T) -> Foo<int>", 14, 1);
+  EXPECT_TRUE(Visitor.runOver(
+      R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T> 
+struct Foo { 
+  Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1); 
+   )cpp",
+      DeductionGuideVisitor::Lang_CXX2a));
+}
+
+TEST(RecursiveASTEnterExitVisitor, DeductionGuideImplicitMode) {
+  DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ true);
+  Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+  Visitor.ExpectMatch("Bar(T) -> Foo<int>", 14, 1);
+  EXPECT_TRUE(Visitor.runOver(
+      R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T> 
+struct Foo { 
+  Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1); 
+   )cpp",
+      DeductionGuideVisitor::Lang_CXX2a));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp
new file mode 100644
index 0000000000000..fee2d35805495
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp
@@ -0,0 +1,40 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtor.cpp ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// A visitor that visits implicit declarations and matches constructors.
+class ImplicitCtorVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitCXXConstructorDecl(CXXConstructorDecl *Ctor) override {
+    if (Ctor->isImplicit()) {  // Was not written in source code
+      if (const CXXRecordDecl* Class = Ctor->getParent()) {
+        Match(Class->getName(), Ctor->getLocation());
+      }
+    }
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsImplicitCopyConstructors) {
+  ImplicitCtorVisitor Visitor;
+  Visitor.ExpectMatch("Simple", 2, 8);
+  // Note: Clang lazily instantiates implicit declarations, so we need
+  // to use them in order to force them to appear in the AST.
+  EXPECT_TRUE(Visitor.runOver(
+      "struct WithCtor { WithCtor(); }; \n"
+      "struct Simple { Simple(); WithCtor w; }; \n"
+      "int main() { Simple s; Simple t(s); }\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp
new file mode 100644
index 0000000000000..258e04dae9893
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp
@@ -0,0 +1,52 @@
+//=- unittest/Tooling/RecursiveASTEnterExitVisitorTests/ImplicitCtorInitializer.cpp -=//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class CXXCtorInitializerVisitor : public ExpectedLocationVisitor {
+public:
+  CXXCtorInitializerVisitor(bool VisitImplicitCode) {
+    ShouldVisitImplicitCode = VisitImplicitCode;
+  }
+
+  bool TraverseConstructorInitializer(CXXCtorInitializer *Init) override {
+    if (!Init->isWritten())
+      VisitedImplicitInitializer = true;
+    Match("initializer", Init->getSourceLocation());
+    return ExpectedLocationVisitor::TraverseConstructorInitializer(Init);
+  }
+
+  bool VisitedImplicitInitializer = false;
+};
+
+// Check to ensure that CXXCtorInitializer is not visited when implicit code
+// should not be visited and that it is visited when implicit code should be
+// visited.
+TEST(RecursiveASTEnterExitVisitor, CXXCtorInitializerVisitNoImplicit) {
+  for (bool VisitImplCode : {true, false}) {
+    CXXCtorInitializerVisitor Visitor(VisitImplCode);
+    Visitor.ExpectMatch("initializer", 7, 17);
+    llvm::StringRef Code = R"cpp(
+        class A {};
+        class B : public A {
+          B() {};
+        };
+        class C : public A {
+          C() : A() {}
+        };
+      )cpp";
+    EXPECT_TRUE(Visitor.runOver(Code, CXXCtorInitializerVisitor::Lang_CXX));
+    EXPECT_EQ(Visitor.VisitedImplicitInitializer, VisitImplCode);
+  }
+}
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp
new file mode 100644
index 0000000000000..fe7c50dd75078
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp
@@ -0,0 +1,36 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrder.cpp -==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CRTPTestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class InitListExprPostOrderVisitor
+    : public CRTPExpectedLocationVisitor<InitListExprPostOrderVisitor> {
+public:
+  bool shouldTraversePostOrder() const { return true; }
+
+  bool VisitInitListExpr(InitListExpr *ILE) {
+    Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, InitListExprIsPostOrderVisitedTwice) {
+  InitListExprPostOrderVisitor Visitor;
+  Visitor.ExpectMatch("syntactic", 2, 21);
+  Visitor.ExpectMatch("semantic", 2, 21);
+  EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+                              "static struct S s = {.x = 0};\n",
+                              InitListExprPostOrderVisitor::Lang_C));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrderNoQueue.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrderNoQueue.cpp
new file mode 100644
index 0000000000000..f97966b357589
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPostOrderNoQueue.cpp
@@ -0,0 +1,40 @@
+//===- InitListExprPostOrderNoQueue.cpp -----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CRTPTestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class InitListExprPostOrderNoQueueVisitor
+    : public CRTPExpectedLocationVisitor<InitListExprPostOrderNoQueueVisitor> {
+public:
+  bool shouldTraversePostOrder() const { return true; }
+
+  bool TraverseInitListExpr(InitListExpr *ILE) {
+    return CRTPExpectedLocationVisitor::TraverseInitListExpr(ILE);
+  }
+
+  bool VisitInitListExpr(InitListExpr *ILE) {
+    Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, InitListExprIsPostOrderNoQueueVisitedTwice) {
+  InitListExprPostOrderNoQueueVisitor Visitor;
+  Visitor.ExpectMatch("syntactic", 2, 21);
+  Visitor.ExpectMatch("semantic", 2, 21);
+  EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+                              "static struct S s = {.x = 0};\n",
+                              InitListExprPostOrderNoQueueVisitor::Lang_C));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp
new file mode 100644
index 0000000000000..f9b4a1461082a
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp
@@ -0,0 +1,48 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrder.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Check to ensure that InitListExpr is visited twice, once each for the
+// syntactic and semantic form.
+class InitListExprPreOrderVisitor : public ExpectedLocationVisitor {
+public:
+  InitListExprPreOrderVisitor(bool VisitImplicitCode) {
+    ShouldVisitImplicitCode = VisitImplicitCode;
+  }
+
+  bool VisitInitListExpr(InitListExpr *ILE) override {
+    Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, InitListExprIsPreOrderVisitedTwice) {
+  InitListExprPreOrderVisitor Visitor(/*VisitImplicitCode=*/true);
+  Visitor.ExpectMatch("syntactic", 2, 21);
+  Visitor.ExpectMatch("semantic", 2, 21);
+  EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+                              "static struct S s = {.x = 0};\n",
+                              InitListExprPreOrderVisitor::Lang_C));
+}
+
+TEST(RecursiveASTEnterExitVisitor, InitListExprVisitedOnceWhenNoImplicit) {
+  InitListExprPreOrderVisitor Visitor(/*VisitImplicitCode=*/false);
+  Visitor.ExpectMatch("syntactic", 2, 21);
+  Visitor.DisallowMatch("semantic", 2, 21);
+  EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+                              "static struct S s = {.x = 0};\n",
+                              InitListExprPreOrderVisitor::Lang_C));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrderNoQueue.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrderNoQueue.cpp
new file mode 100644
index 0000000000000..6cf21ddeb23b2
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/InitListExprPreOrderNoQueue.cpp
@@ -0,0 +1,37 @@
+//===- InitListExprPreOrderNoQueue.cpp ------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class InitListExprPreOrderNoQueueVisitor : public ExpectedLocationVisitor {
+public:
+  bool TraverseInitListExpr(InitListExpr *ILE) override {
+    return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
+  }
+
+  bool VisitInitListExpr(InitListExpr *ILE) override {
+    Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, InitListExprIsPreOrderNoQueueVisitedTwice) {
+  InitListExprPreOrderNoQueueVisitor Visitor;
+  Visitor.ExpectMatch("syntactic", 2, 21);
+  Visitor.ExpectMatch("semantic", 2, 21);
+  EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
+                              "static struct S s = {.x = 0};\n",
+                              InitListExprPreOrderNoQueueVisitor::Lang_C));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp
new file mode 100644
index 0000000000000..720dc480832fd
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp
@@ -0,0 +1,32 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/IntegerLiteral.cpp -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Check to ensure that implicit default argument expressions are visited.
+class IntegerLiteralVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitIntegerLiteral(IntegerLiteral *IL) override {
+    Match("literal", IL->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, DefaultArgumentsAreVisited) {
+  IntegerLiteralVisitor Visitor;
+  Visitor.ExpectMatch("literal", 1, 15, 2);
+  EXPECT_TRUE(Visitor.runOver("int f(int i = 1);\n"
+                              "static int k = f();\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp
new file mode 100644
index 0000000000000..1213426ff6d48
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp
@@ -0,0 +1,34 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/LambdaDefaultCapture.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Matches the (optional) capture-default of a lambda-introducer.
+class LambdaDefaultCaptureVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitLambdaExpr(LambdaExpr *Lambda) override {
+    if (Lambda->getCaptureDefault() != LCD_None) {
+      Match("", Lambda->getCaptureDefaultLoc());
+    }
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, HasCaptureDefaultLoc) {
+  LambdaDefaultCaptureVisitor Visitor;
+  Visitor.ExpectMatch("", 1, 20);
+  EXPECT_TRUE(Visitor.runOver("void f() { int a; [=]{a;}; }",
+                              LambdaDefaultCaptureVisitor::Lang_CXX11));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp
new file mode 100644
index 0000000000000..a230b51cb81ab
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp
@@ -0,0 +1,99 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/LambdaExpr.cpp -----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include "llvm/TargetParser/Host.h"
+#include <stack>
+
+using namespace clang;
+
+namespace {
+
+class LambdaExprVisitor : public ExpectedLocationVisitor {
+public:
+  LambdaExprVisitor() { ShouldVisitImplicitCode = false; }
+
+  bool VisitLambdaExpr(LambdaExpr *Lambda) override {
+    PendingBodies.push(Lambda->getBody());
+    PendingClasses.push(Lambda->getLambdaClass());
+    Match("", Lambda->getIntroducerRange().getBegin());
+    return true;
+  }
+  /// For each call to VisitLambdaExpr, we expect a subsequent call to visit
+  /// the body (and maybe the lambda class, which is implicit).
+  bool VisitStmt(Stmt *S) override {
+    if (!PendingBodies.empty() && S == PendingBodies.top())
+      PendingBodies.pop();
+    return true;
+  }
+  bool VisitDecl(Decl *D) override {
+    if (!PendingClasses.empty() && D == PendingClasses.top())
+      PendingClasses.pop();
+    return true;
+  }
+  /// Determine whether parts of lambdas (VisitLambdaExpr) were later traversed.
+  bool allBodiesHaveBeenTraversed() const { return PendingBodies.empty(); }
+  bool allClassesHaveBeenTraversed() const { return PendingClasses.empty(); }
+
+private:
+  std::stack<Stmt *> PendingBodies;
+  std::stack<Decl *> PendingClasses;
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsLambdaExpr) {
+  LambdaExprVisitor Visitor;
+  Visitor.ExpectMatch("", 1, 12);
+  EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
+                              LambdaExprVisitor::Lang_CXX11));
+  EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+  EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTEnterExitVisitor, LambdaInLambda) {
+  LambdaExprVisitor Visitor;
+  Visitor.ExpectMatch("", 1, 12);
+  Visitor.ExpectMatch("", 1, 16);
+  EXPECT_TRUE(Visitor.runOver("void f() { []{ []{ return; }; }(); }",
+                              LambdaExprVisitor::Lang_CXX11));
+  EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+  EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTEnterExitVisitor, TopLevelLambda) {
+  LambdaExprVisitor Visitor;
+  Visitor.ShouldVisitImplicitCode = true;
+  Visitor.ExpectMatch("", 1, 10);
+  Visitor.ExpectMatch("", 1, 14);
+  EXPECT_TRUE(Visitor.runOver("auto x = []{ [] {}; };",
+                              LambdaExprVisitor::Lang_CXX11));
+  EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+  EXPECT_TRUE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsLambdaExprAndImplicitClass) {
+  LambdaExprVisitor Visitor;
+  Visitor.ShouldVisitImplicitCode = true;
+  Visitor.ExpectMatch("", 1, 12);
+  EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
+                              LambdaExprVisitor::Lang_CXX11));
+  EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+  EXPECT_TRUE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTEnterExitVisitor, VisitsAttributedLambdaExpr) {
+  if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).isPS())
+    GTEST_SKIP(); // PS4/PS5 do not support fastcall.
+  LambdaExprVisitor Visitor;
+  Visitor.ExpectMatch("", 1, 12);
+  EXPECT_TRUE(Visitor.runOver(
+      "void f() { [] () __attribute__ (( fastcall )) { return; }(); }",
+      LambdaExprVisitor::Lang_CXX14));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp
new file mode 100644
index 0000000000000..b75275cdbd453
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp
@@ -0,0 +1,52 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/LambdaTemplateParams.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Matches (optional) explicit template parameters.
+class LambdaTemplateParametersVisitor : public ExpectedLocationVisitor {
+public:
+  LambdaTemplateParametersVisitor() { ShouldVisitImplicitCode = false; }
+
+  bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) override {
+    EXPECT_FALSE(D->isImplicit());
+    Match(D->getName(), D->getBeginLoc());
+    return true;
+  }
+
+  bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) override {
+    EXPECT_FALSE(D->isImplicit());
+    Match(D->getName(), D->getBeginLoc());
+    return true;
+  }
+
+  bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) override {
+    EXPECT_FALSE(D->isImplicit());
+    Match(D->getName(), D->getBeginLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsLambdaExplicitTemplateParameters) {
+  LambdaTemplateParametersVisitor Visitor;
+  Visitor.ExpectMatch("T",  2, 15);
+  Visitor.ExpectMatch("I",  2, 24);
+  Visitor.ExpectMatch("TT", 2, 31);
+  EXPECT_TRUE(Visitor.runOver(
+      "void f() { \n"
+      "  auto l = []<class T, int I, template<class> class TT>(auto p) { }; \n"
+      "}",
+      LambdaTemplateParametersVisitor::Lang_CXX2a));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp
new file mode 100644
index 0000000000000..a05872a30cecf
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp
@@ -0,0 +1,58 @@
+//===- unittest/Tooling/EnterExitRecursiveASTEnterExitVisitorTests/MemberPointerTypeLoc.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang;
+
+namespace {
+
+class MemberPointerTypeLocVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) override {
+    if (!TL)
+      return true;
+    Match(TL.getDecl()->getName(), TL.getNameLoc());
+    return true;
+  }
+  bool VisitRecordTypeLoc(RecordTypeLoc RTL) override {
+    if (!RTL)
+      return true;
+    Match(RTL.getDecl()->getName(), RTL.getNameLoc());
+    return true;
+  }
+};
+
+TEST(EnterExitRecursiveASTEnterExitVisitor, VisitTypeLocInMemberPointerTypeLoc) {
+  MemberPointerTypeLocVisitor Visitor;
+  Visitor.ExpectMatch("Bar", 4, 36);
+  Visitor.ExpectMatch("T", 7, 23);
+  llvm::StringLiteral Code = R"cpp(
+     class Bar { void func(int); };
+     class Foo {
+       void bind(const char*, void(Bar::*Foo)(int)) {}
+
+       template<typename T>
+       void test(void(T::*Foo)());
+     };
+  )cpp";
+  EXPECT_TRUE(Visitor.runOver(Code));
+}
+
+TEST(EnterExitRecursiveASTEnterExitVisitor, NoCrash) {
+  MemberPointerTypeLocVisitor Visitor;
+  llvm::StringLiteral Code = R"cpp(
+     // MemberPointerTypeLoc.getClassTInfo() is null.
+     class a(b(a::*)) class
+  )cpp";
+  EXPECT_FALSE(Visitor.runOver(Code));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp
new file mode 100644
index 0000000000000..3fc9fef096753
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp
@@ -0,0 +1,73 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/NestedNameSpecifiers.cpp -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+// Check to ensure that nested name specifiers are visited.
+class NestedNameSpecifiersVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitRecordTypeLoc(RecordTypeLoc RTL) override {
+    if (!RTL)
+      return true;
+    Match(RTL.getDecl()->getName(), RTL.getNameLoc());
+    return true;
+  }
+
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) override {
+    if (!NNS)
+      return true;
+    if (const NamespaceDecl *ND =
+            NNS.getNestedNameSpecifier()->getAsNamespace())
+      Match(ND->getName(), NNS.getLocalBeginLoc());
+    return ExpectedLocationVisitor::TraverseNestedNameSpecifierLoc(NNS);
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor,
+     NestedNameSpecifiersForTemplateSpecializationsAreVisited) {
+  StringRef Source = R"(
+namespace ns {
+struct Outer {
+    template<typename T, typename U>
+    struct Nested { };
+
+    template<typename T>
+    static T x;
+};
+}
+
+template<>
+struct ns::Outer::Nested<int, int>;
+
+template<>
+struct ns::Outer::Nested<int, int> { };
+
+template<typename T>
+struct ns::Outer::Nested<int, T> { };
+
+template<>
+int ns::Outer::x<int> = 0;
+)";
+  NestedNameSpecifiersVisitor Visitor;
+  Visitor.ExpectMatch("ns", 13, 8);
+  Visitor.ExpectMatch("ns", 16, 8);
+  Visitor.ExpectMatch("ns", 19, 8);
+  Visitor.ExpectMatch("ns", 22, 5);
+  Visitor.ExpectMatch("Outer", 13, 12);
+  Visitor.ExpectMatch("Outer", 16, 12);
+  Visitor.ExpectMatch("Outer", 19, 12);
+  Visitor.ExpectMatch("Outer", 22, 9);
+  EXPECT_TRUE(Visitor.runOver(Source, NestedNameSpecifiersVisitor::Lang_CXX14));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ParenExpr.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ParenExpr.cpp
new file mode 100644
index 0000000000000..d225d30178493
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/ParenExpr.cpp
@@ -0,0 +1,30 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/ParenExpr.cpp ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class ParenExprVisitor : public ExpectedLocationVisitor {
+public:
+  bool VisitParenExpr(ParenExpr *Parens) override {
+    Match("", Parens->getExprLoc());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsParensDuringDataRecursion) {
+  ParenExprVisitor Visitor;
+  Visitor.ExpectMatch("", 1, 9);
+  EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TemplateArgumentLocTraverser.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TemplateArgumentLocTraverser.cpp
new file mode 100644
index 0000000000000..dcd810853f6c1
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TemplateArgumentLocTraverser.cpp
@@ -0,0 +1,38 @@
+//===- TemplateArgumentLocTraverser.cpp -----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class TemplateArgumentLocTraverser : public ExpectedLocationVisitor {
+public:
+  bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) override {
+    std::string ArgStr;
+    llvm::raw_string_ostream Stream(ArgStr);
+    const TemplateArgument &Arg = ArgLoc.getArgument();
+
+    Arg.print(Context->getPrintingPolicy(), Stream, /*IncludeType*/ true);
+    Match(ArgStr, ArgLoc.getLocation());
+    return ExpectedLocationVisitor::TraverseTemplateArgumentLoc(ArgLoc);
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, VisitsClassTemplateTemplateParmDefaultArgument) {
+  TemplateArgumentLocTraverser Visitor;
+  Visitor.ExpectMatch("X", 2, 40);
+  EXPECT_TRUE(Visitor.runOver(
+    "template<typename T> class X;\n"
+    "template<template <typename> class T = X> class Y;\n"
+    "template<template <typename> class T> class Y {};\n"));
+}
+
+} // end anonymous namespace
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TraversalScope.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TraversalScope.cpp
new file mode 100644
index 0000000000000..4922f3190a835
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTests/TraversalScope.cpp
@@ -0,0 +1,58 @@
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTests/TraversalScope.cpp -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/AST/RecursiveASTEnterExitVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+class Visitor : public ExpectedLocationVisitor {
+public:
+  Visitor(ASTContext *Context) { this->Context = Context; }
+
+  bool VisitTranslationUnitDecl(TranslationUnitDecl *D) override {
+    auto &SM = D->getParentASTContext().getSourceManager();
+    Match("TU", SM.getLocForStartOfFile(SM.getMainFileID()));
+    return true;
+  }
+
+  bool VisitNamedDecl(NamedDecl *D) override {
+    if (!D->isImplicit())
+      Match(D->getName(), D->getLocation());
+    return true;
+  }
+};
+
+TEST(RecursiveASTEnterExitVisitor, RespectsTraversalScope) {
+  auto AST = tooling::buildASTFromCode(
+      R"cpp(
+struct foo {
+  struct bar {
+    struct baz {};
+  };
+};
+      )cpp",
+      "foo.cpp", std::make_shared<PCHContainerOperations>());
+  auto &Ctx = AST->getASTContext();
+  auto &TU = *Ctx.getTranslationUnitDecl();
+  auto &Foo = *TU.lookup(&Ctx.Idents.get("foo")).front();
+  auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get("bar")).front();
+
+  Ctx.setTraversalScope({&Bar});
+
+  Visitor V(&Ctx);
+  V.ExpectMatch("TU", 1, 1);
+  V.DisallowMatch("foo", 2, 8);
+  V.ExpectMatch("bar", 3, 10);
+  V.ExpectMatch("baz", 4, 12);
+  V.TraverseAST(Ctx);
+}
+
+} // end anonymous namespace

>From 0a63b835c3e1b0b43287fad855155729dfb7312e Mon Sep 17 00:00:00 2001
From: "ct.clmsn" <ctaylor at tactcomplabs.com>
Date: Thu, 17 Apr 2025 09:14:59 -0400
Subject: [PATCH 2/2] corrections wrt type names

Signed-off-by: ct.clmsn <ctaylor at tactcomplabs.com>
---
 ...rsiveASTEnterExitVisitorTestDeclVisitor.cpp | 18 +++++++++---------
 ...ASTEnterExitVisitorTestPostOrderVisitor.cpp |  8 ++++----
 ...veASTEnterExitVisitorTestTypeLocVisitor.cpp | 18 +++++++++---------
 3 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
index b087faee6256f..075d588f30f34 100644
--- a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp
@@ -1,4 +1,4 @@
-//===- unittest/Tooling/EnterExitRecursiveASTVisitorTestDeclVisitor.cpp ------------===//
+//===- unittest/Tooling/RecursiveASTEnterExitVisitorTestDeclVisitor.cpp ------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -21,7 +21,7 @@ class VarDeclVisitor : public ExpectedLocationVisitor {
   }
 };
 
-TEST(EnterExitRecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
+TEST(RecursiveASTEnterExitVisitor, VisitsCXXForRangeStmtLoopVariable) {
   VarDeclVisitor Visitor;
   Visitor.ExpectMatch("i", 2, 17);
   EXPECT_TRUE(Visitor.runOver(
@@ -42,7 +42,7 @@ class ParmVarDeclVisitorForImplicitCode : public ExpectedLocationVisitor {
 
 // Test RAV visits parameter variable declaration of the implicit
 // copy assignment operator and implicit copy constructor.
-TEST(EnterExitRecursiveASTVisitor, VisitsParmVarDeclForImplicitCode) {
+TEST(RecursiveASTEnterExitVisitor, VisitsParmVarDeclForImplicitCode) {
   ParmVarDeclVisitorForImplicitCode Visitor;
   // Match parameter variable name of implicit copy assignment operator and
   // implicit copy constructor.
@@ -71,7 +71,7 @@ class NamedDeclVisitor : public ExpectedLocationVisitor {
   }
 };
 
-TEST(EnterExitRecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
+TEST(RecursiveASTEnterExitVisitor, VisitsPartialTemplateSpecialization) {
   // From cfe-commits/Week-of-Mon-20100830/033998.html
   // Contrary to the approach suggested in that email, we visit all
   // specializations when we visit the primary template.  Visiting them when we
@@ -87,7 +87,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
     "A<char*> acp;\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
+TEST(RecursiveASTEnterExitVisitor, VisitsUndefinedClassTemplateSpecialization) {
   NamedDeclVisitor Visitor;
   Visitor.ExpectMatch("A<int>", 1, 29);
   EXPECT_TRUE(Visitor.runOver(
@@ -95,7 +95,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
     "A<int> *p;\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
+TEST(RecursiveASTEnterExitVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
   NamedDeclVisitor Visitor;
   Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
   EXPECT_TRUE(Visitor.runOver(
@@ -105,7 +105,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecializat
     "A<int>::B<char> *p;\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
+TEST(RecursiveASTEnterExitVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
   NamedDeclVisitor Visitor;
   Visitor.ExpectMatch("A<int>", 1, 26);
   EXPECT_TRUE(Visitor.runOver(
@@ -113,7 +113,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization
     "int k = A<int>();\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
+TEST(RecursiveASTEnterExitVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
   NamedDeclVisitor Visitor;
   Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
   EXPECT_TRUE(Visitor.runOver(
@@ -123,7 +123,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpeciali
     "int k = A<int>::B<char>();\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, NoRecursionInSelfFriend) {
+TEST(RecursiveASTEnterExitVisitor, NoRecursionInSelfFriend) {
   // From cfe-commits/Week-of-Mon-20100830/033977.html
   NamedDeclVisitor Visitor;
   Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
index 5c97d0e475640..af19419709e2e 100644
--- a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestPostOrderVisitor.cpp
@@ -1,4 +1,4 @@
-//===- unittests/Tooling/EnterExitRecursiveASTVisitorPostOrderASTVisitor.cpp -------===//
+//===- unittests/Tooling/RecursiveASTEnterExitVisitorPostOrderASTVisitor.cpp -------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 //
 // This file contains tests for the post-order traversing functionality
-// of EnterExitRecursiveASTVisitor.
+// of RecursiveASTEnterExitVisitor.
 //
 //===----------------------------------------------------------------------===//
 
@@ -72,7 +72,7 @@ class RecordingVisitor : public CRTPTestVisitor<RecordingVisitor> {
 };
 } // namespace
 
-TEST(EnterExitRecursiveASTVisitor, PostOrderTraversal) {
+TEST(RecursiveASTEnterExitVisitor, PostOrderTraversal) {
   // We traverse the translation unit and store all visited nodes.
   RecordingVisitor Visitor(true);
   Visitor.runOver("class A {\n"
@@ -94,7 +94,7 @@ TEST(EnterExitRecursiveASTVisitor, PostOrderTraversal) {
   }
 }
 
-TEST(EnterExitRecursiveASTVisitor, NoPostOrderTraversal) {
+TEST(RecursiveASTEnterExitVisitor, NoPostOrderTraversal) {
   // We traverse the translation unit and store all visited nodes.
   RecordingVisitor Visitor(false);
   Visitor.runOver("class A {\n"
diff --git a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
index 4da5288c4254b..946bcc72a7542 100644
--- a/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
+++ b/clang/unittests/Tooling/RecursiveASTEnterExitVisitorTestTypeLocVisitor.cpp
@@ -21,13 +21,13 @@ class TypeLocVisitor : public ExpectedLocationVisitor {
   }
 };
 
-TEST(EnterExitRecursiveASTVisitor, VisitsBaseClassDeclarations) {
+TEST(RecursiveASTEnterExitVisitor, VisitsBaseClassDeclarations) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("class X", 1, 30);
   EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
+TEST(RecursiveASTEnterExitVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("class X", 3, 18);
   EXPECT_TRUE(Visitor.runOver(
@@ -36,7 +36,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass
     "class Y : public X {};"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
+TEST(RecursiveASTEnterExitVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("class X", 2, 18);
   EXPECT_TRUE(Visitor.runOver(
@@ -44,7 +44,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerCla
     "class Y : public X { class Z; };"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
+TEST(RecursiveASTEnterExitVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("X<Y>", 2, 18, 2);
   EXPECT_TRUE(Visitor.runOver(
@@ -52,7 +52,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType)
     "class Y : public X<Y> {};"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
+TEST(RecursiveASTEnterExitVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("class X", 2, 23);
   EXPECT_TRUE(Visitor.runOver(
@@ -61,7 +61,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
     "template<typename T> class Y {};\n"));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsCompoundLiteralType) {
+TEST(RecursiveASTEnterExitVisitor, VisitsCompoundLiteralType) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("struct S", 1, 26);
   EXPECT_TRUE(Visitor.runOver(
@@ -69,7 +69,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsCompoundLiteralType) {
       TypeLocVisitor::Lang_C));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsObjCPropertyType) {
+TEST(RecursiveASTEnterExitVisitor, VisitsObjCPropertyType) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("NSNumber", 2, 33);
   EXPECT_TRUE(Visitor.runOver(
@@ -78,7 +78,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitsObjCPropertyType) {
       TypeLocVisitor::Lang_OBJC));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitInvalidType) {
+TEST(RecursiveASTEnterExitVisitor, VisitInvalidType) {
   TypeLocVisitor Visitor;
   // FIXME: It would be nice to have information about subtypes of invalid type
   //Visitor.ExpectMatch("typeof(struct F *) []", 1, 1);
@@ -89,7 +89,7 @@ TEST(EnterExitRecursiveASTVisitor, VisitInvalidType) {
       TypeLocVisitor::Lang_C));
 }
 
-TEST(EnterExitRecursiveASTVisitor, VisitsUsingEnumType) {
+TEST(RecursiveASTEnterExitVisitor, VisitsUsingEnumType) {
   TypeLocVisitor Visitor;
   Visitor.ExpectMatch("::A", 2, 12);
   EXPECT_TRUE(Visitor.runOver("enum class A {}; \n"



More information about the cfe-commits mailing list