r274348 - Add postorder traversal support to the RecursiveASTVisitor.
Vassil Vassilev via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 1 06:28:31 PDT 2016
Author: vvassilev
Date: Fri Jul 1 08:28:31 2016
New Revision: 274348
URL: http://llvm.org/viewvc/llvm-project?rev=274348&view=rev
Log:
Add postorder traversal support to the RecursiveASTVisitor.
This feature needs to be explicitly enabled by overriding shouldTraversePostOrder()
as it has performance drawbacks for the iterative Stmt-traversal.
Patch by Raphael Isemann!
Reviewed by Richard Smith and Benjamin Kramer.
Modified:
cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
cfe/trunk/unittests/AST/CMakeLists.txt
Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecursiveASTVisitor.h?rev=274348&r1=274347&r2=274348&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h (original)
+++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h Fri Jul 1 08:28:31 2016
@@ -72,8 +72,8 @@ namespace clang {
return false; \
} while (0)
-/// \brief A class that does preorder depth-first traversal on the
-/// entire Clang AST and visits each node.
+/// \brief A class that does preordor 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);
@@ -133,6 +133,10 @@ namespace clang {
/// 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 overriden
+/// to return \c true.
template <typename Derived> class RecursiveASTVisitor {
public:
/// A queue used for performing data recursion over statements.
@@ -158,6 +162,9 @@ public:
/// code, e.g., implicit constructors and destructors.
bool shouldVisitImplicitCode() const { return false; }
+ /// \brief Return whether this visitor should traverse post-order.
+ bool shouldTraversePostOrder() const { return false; }
+
/// \brief Recursively visit a statement or expression, by
/// dispatching to Traverse*() based on the argument's dynamic type.
///
@@ -349,7 +356,7 @@ public:
bool TraverseUnary##NAME(UnaryOperator *S, \
DataRecursionQueue *Queue = nullptr) { \
TRY_TO(WalkUpFromUnary##NAME(S)); \
- TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getSubExpr()); \
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getSubExpr()); \
return true; \
} \
bool WalkUpFromUnary##NAME(UnaryOperator *S) { \
@@ -367,9 +374,10 @@ public:
// (they're all opcodes in BinaryOperator) but do have visitors.
#define GENERAL_BINOP_FALLBACK(NAME, BINOP_TYPE) \
bool TraverseBin##NAME(BINOP_TYPE *S, DataRecursionQueue *Queue = nullptr) { \
- TRY_TO(WalkUpFromBin##NAME(S)); \
- TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getLHS()); \
- TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRHS()); \
+ if (!getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFromBin##NAME(S)); \
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getLHS()); \
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRHS()); \
return true; \
} \
bool WalkUpFromBin##NAME(BINOP_TYPE *S) { \
@@ -499,6 +507,7 @@ private:
bool VisitOMPClauseWithPostUpdate(OMPClauseWithPostUpdate *Node);
bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
+ bool PostVisitStmt(Stmt *S);
};
template <typename Derived>
@@ -556,6 +565,24 @@ bool RecursiveASTVisitor<Derived>::dataT
#undef DISPATCH_STMT
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::PostVisitStmt(Stmt *S) {
+ switch (S->getStmtClass()) {
+ case Stmt::NoStmtClass:
+ break;
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT) \
+ case Stmt::CLASS##Class: \
+ TRY_TO(WalkUpFrom##CLASS(static_cast<CLASS *>(S))); break;
+#include "clang/AST/StmtNodes.inc"
+ }
+
+ return true;
+}
+
+#undef DISPATCH_STMT
+
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseStmt(Stmt *S,
DataRecursionQueue *Queue) {
@@ -577,6 +604,9 @@ bool RecursiveASTVisitor<Derived>::Trave
if (Visited) {
LocalQueue.pop_back();
TRY_TO(dataTraverseStmtPost(CurrS));
+ if (getDerived().shouldTraversePostOrder()) {
+ TRY_TO(PostVisitStmt(CurrS));
+ }
continue;
}
@@ -874,8 +904,11 @@ bool RecursiveASTVisitor<Derived>::Trave
#define DEF_TRAVERSE_TYPE(TYPE, CODE) \
template <typename Derived> \
bool RecursiveASTVisitor<Derived>::Traverse##TYPE(TYPE *T) { \
- TRY_TO(WalkUpFrom##TYPE(T)); \
+ if (!getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##TYPE(T)); \
{ CODE; } \
+ if (getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##TYPE(T)); \
return true; \
}
@@ -1278,10 +1311,16 @@ bool RecursiveASTVisitor<Derived>::Trave
#define DEF_TRAVERSE_DECL(DECL, CODE) \
template <typename Derived> \
bool RecursiveASTVisitor<Derived>::Traverse##DECL(DECL *D) { \
- TRY_TO(WalkUpFrom##DECL(D)); \
+ bool ShouldVisitChildren = true; \
+ bool ReturnValue = true; \
+ if (!getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##DECL(D)); \
{ CODE; } \
- TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D))); \
- return true; \
+ if (ReturnValue && ShouldVisitChildren) \
+ TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D))); \
+ if (ReturnValue && getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##DECL(D)); \
+ return ReturnValue; \
}
DEF_TRAVERSE_DECL(AccessSpecDecl, {})
@@ -1295,18 +1334,12 @@ DEF_TRAVERSE_DECL(BlockDecl, {
TRY_TO(TraverseStmt(I.getCopyExpr()));
}
}
- // This return statement makes sure the traversal of nodes in
- // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
- // is skipped - don't remove it.
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(CapturedDecl, {
TRY_TO(TraverseStmt(D->getBody()));
- // This return statement makes sure the traversal of nodes in
- // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
- // is skipped - don't remove it.
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(EmptyDecl, {})
@@ -1376,11 +1409,7 @@ DEF_TRAVERSE_DECL(NamespaceAliasDecl, {
// We shouldn't traverse an aliased namespace, since it will be
// defined (and, therefore, traversed) somewhere else.
- //
- // This return statement makes sure the traversal of nodes in
- // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
- // is skipped - don't remove it.
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl.
@@ -1435,7 +1464,7 @@ DEF_TRAVERSE_DECL(ObjCMethodDecl, {
if (D->isThisDeclarationADefinition()) {
TRY_TO(TraverseStmt(D->getBody()));
}
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(ObjCTypeParamDecl, {
@@ -1452,7 +1481,7 @@ DEF_TRAVERSE_DECL(ObjCPropertyDecl, {
TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
else
TRY_TO(TraverseType(D->getType()));
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(UsingDecl, {
@@ -1854,19 +1883,22 @@ bool RecursiveASTVisitor<Derived>::Trave
DEF_TRAVERSE_DECL(FunctionDecl, {
// We skip decls_begin/decls_end, which are already covered by
// TraverseFunctionHelper().
- return TraverseFunctionHelper(D);
+ ShouldVisitChildren = false;
+ ReturnValue = TraverseFunctionHelper(D);
})
DEF_TRAVERSE_DECL(CXXMethodDecl, {
// We skip decls_begin/decls_end, which are already covered by
// TraverseFunctionHelper().
- return TraverseFunctionHelper(D);
+ ShouldVisitChildren = false;
+ ReturnValue = TraverseFunctionHelper(D);
})
DEF_TRAVERSE_DECL(CXXConstructorDecl, {
// We skip decls_begin/decls_end, which are already covered by
// TraverseFunctionHelper().
- return TraverseFunctionHelper(D);
+ ShouldVisitChildren = false;
+ ReturnValue = TraverseFunctionHelper(D);
})
// CXXConversionDecl is the declaration of a type conversion operator.
@@ -1874,13 +1906,15 @@ DEF_TRAVERSE_DECL(CXXConstructorDecl, {
DEF_TRAVERSE_DECL(CXXConversionDecl, {
// We skip decls_begin/decls_end, which are already covered by
// TraverseFunctionHelper().
- return TraverseFunctionHelper(D);
+ ShouldVisitChildren = false;
+ ReturnValue = TraverseFunctionHelper(D);
})
DEF_TRAVERSE_DECL(CXXDestructorDecl, {
// We skip decls_begin/decls_end, which are already covered by
// TraverseFunctionHelper().
- return TraverseFunctionHelper(D);
+ ShouldVisitChildren = false;
+ ReturnValue = TraverseFunctionHelper(D);
})
template <typename Derived>
@@ -1932,12 +1966,19 @@ DEF_TRAVERSE_DECL(ParmVarDecl, {
template <typename Derived> \
bool RecursiveASTVisitor<Derived>::Traverse##STMT( \
STMT *S, DataRecursionQueue *Queue) { \
- TRY_TO(WalkUpFrom##STMT(S)); \
+ bool ShouldVisitChildren = true; \
+ bool ReturnValue = true; \
+ if (!getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##STMT(S)); \
{ CODE; } \
- for (Stmt *SubStmt : S->children()) { \
- TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt); \
+ if (ShouldVisitChildren) { \
+ for (Stmt *SubStmt : S->children()) { \
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt); \
+ } \
} \
- return true; \
+ if (!Queue && ReturnValue && getDerived().shouldTraversePostOrder()) \
+ TRY_TO(WalkUpFrom##STMT(S)); \
+ return ReturnValue; \
}
DEF_TRAVERSE_STMT(GCCAsmStmt, {
@@ -1974,7 +2015,7 @@ DEF_TRAVERSE_STMT(DeclStmt, {
// initializer]'. The decls above already traverse over the
// initializers, so we don't have to do it again (which
// children() would do).
- return true;
+ ShouldVisitChildren = false;
})
// These non-expr stmts (most of them), do not need any action except
@@ -2006,7 +2047,7 @@ DEF_TRAVERSE_STMT(CXXForRangeStmt, {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRangeInit());
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
// Visit everything else only if shouldVisitImplicitCode().
- return true;
+ ShouldVisitChildren = false;
}
})
DEF_TRAVERSE_STMT(MSDependentExistsStmt, {
@@ -2083,7 +2124,11 @@ template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseSynOrSemInitListExpr(
InitListExpr *S, DataRecursionQueue *Queue) {
if (S) {
- TRY_TO(WalkUpFromInitListExpr(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);
@@ -2103,7 +2148,7 @@ DEF_TRAVERSE_STMT(InitListExpr, {
S->isSemanticForm() ? S->getSyntacticForm() : S, Queue));
TRY_TO(TraverseSynOrSemInitListExpr(
S->isSemanticForm() ? S : S->getSemanticForm(), Queue));
- return true;
+ ShouldVisitChildren = false;
})
// GenericSelectionExpr is a special case because the types and expressions
@@ -2116,7 +2161,7 @@ DEF_TRAVERSE_STMT(GenericSelectionExpr,
TRY_TO(TraverseTypeLoc(TS->getTypeLoc()));
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAssocExpr(i));
}
- return true;
+ ShouldVisitChildren = false;
})
// PseudoObjectExpr is a special case because of the weirdness with
@@ -2131,7 +2176,7 @@ DEF_TRAVERSE_STMT(PseudoObjectExpr, {
sub = OVE->getSourceExpr();
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(sub);
}
- return true;
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, {
@@ -2235,7 +2280,8 @@ DEF_TRAVERSE_STMT(LambdaExpr, {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(NE);
}
- return TRAVERSE_STMT_BASE(LambdaBody, LambdaExpr, S, Queue);
+ ReturnValue = TRAVERSE_STMT_BASE(LambdaBody, LambdaExpr, S, Queue);
+ ShouldVisitChildren = false;
})
DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
@@ -2362,25 +2408,25 @@ DEF_TRAVERSE_STMT(AtomicExpr, {})
DEF_TRAVERSE_STMT(CoroutineBodyStmt, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
- return true;
+ ShouldVisitChildren = false;
}
})
DEF_TRAVERSE_STMT(CoreturnStmt, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
- return true;
+ ShouldVisitChildren = false;
}
})
DEF_TRAVERSE_STMT(CoawaitExpr, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
- return true;
+ ShouldVisitChildren = false;
}
})
DEF_TRAVERSE_STMT(CoyieldExpr, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
- return true;
+ ShouldVisitChildren = false;
}
})
Modified: cfe/trunk/unittests/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/CMakeLists.txt?rev=274348&r1=274347&r2=274348&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/CMakeLists.txt (original)
+++ cfe/trunk/unittests/AST/CMakeLists.txt Fri Jul 1 08:28:31 2016
@@ -14,6 +14,7 @@ add_clang_unittest(ASTTests
EvaluateAsRValueTest.cpp
ExternalASTSourceTest.cpp
NamedDeclPrinterTest.cpp
+ PostOrderASTVisitor.cpp
SourceLocationTest.cpp
StmtPrinterTest.cpp
)
More information about the cfe-commits
mailing list