OCLint.6.RecursiveASTVisitor-class-from-clangAST-library
RecursiveASTVisitor class from clangAST library
1. What's RecursiveASTVisitor?
RecursiveASTVisitor class is defined at the file ${LLVM_PROJECT_PATH}/clang/include/clang/AST/RecursiveASTVisitor.h.
2. Macros in RecursiveASTVisitor
There are many macros in the RecursiveASTVisitor.h file:
2.1 TRAVERSE_STMT_BASE(NAME, CLASS, VAR, QUEUE)
If this class(RecursiveASTVisitor) and Derived class have the same "Traverse##NAME" function, then
convert '*this' into Derived type and call "Traverse##NAME" function with 'VAR' and 'QUEUE'.
If not, convert '*this' into Derived type and call "Traverse##NAME" function with 'VAR'.
1 // Traverse the given statement. If the most-derived traverse function takes a 2 // data recursion queue, pass it on; otherwise, discard it. Note that the 3 // first branch of this conditional must compile whether or not the derived 4 // class can take a queue, so if we're taking the second arm, make the first 5 // arm call our function rather than the derived class version. 6 #define TRAVERSE_STMT_BASE(NAME, CLASS, VAR, QUEUE) \ 7 (::clang::detail::has_same_member_pointer_type< \ 8 decltype(&RecursiveASTVisitor::Traverse##NAME), \ 9 decltype(&Derived::Traverse##NAME)>::value \ 10 ? static_cast<std::conditional_t< \ 11 ::clang::detail::has_same_member_pointer_type< \ 12 decltype(&RecursiveASTVisitor::Traverse##NAME), \ 13 decltype(&Derived::Traverse##NAME)>::value, \ 14 Derived &, RecursiveASTVisitor &>>(*this) \ 15 .Traverse##NAME(static_cast<CLASS *>(VAR), QUEUE) \ 16 : getDerived().Traverse##NAME(static_cast<CLASS *>(VAR)))
2.2 STMT(CLASS, PARENT) and StmtNodes.inc
Using the StmtNodes.inc file and STMT() macro define the Traverse##CLASS() function.
2.2.1 CLASS
The 'CLASS' would be any statement: BreakStmt, CXXCatchStmt, CXXForRangeStmt, CXXTryStmt,
CapturedStmt, CompoundStmt, ContinueStmt, CoroutineBodyStmt, DeclStmt, DoStmt, ForStmt,
GotoStmt, IfStmt, IndirectGotoStmt, MSDependentExistsStmt, NullStmt, ObjCAtCatchStmt, ObjCAtFinallyStmt,
ObjCAtSynchronizedStmt, ObjCAtThrowStmt, ObjCAtTryStmt, ObjCAutoreleasePoolStmt,
ObjCForCollectionStmt, ReturnStmt, CaseStmt, DefaultStmt, AttributedStmt, LabelStmt, WhileStmt, ... ...
1 // Declare Traverse*() for all concrete Stmt classes. 2 #define ABSTRACT_STMT(STMT) 3 #define STMT(CLASS, PARENT) \ 4 bool Traverse##CLASS(CLASS *S, DataRecursionQueue *Queue = nullptr); 5 #include "clang/AST/StmtNodes.inc"
2.3 Define the WalkUpFrom##Class() and Visit##CLASS() functions
By defining the WalkUpFrom##CLASS() and Visit##CLASS() functions to provide the defualt implementations for
RecursiveASTVisitor class. The values of 'CLASS' are the same as "2.2.1 CLASS".
1 // Define WalkUpFrom*() and empty Visit*() for all Stmt classes. 2 bool WalkUpFromStmt(Stmt *S) { return getDerived().VisitStmt(S); } 3 bool VisitStmt(Stmt *S) { return true; } 4 #define STMT(CLASS, PARENT) \ 5 bool WalkUpFrom##CLASS(CLASS *S) { \ 6 TRY_TO(WalkUpFrom##PARENT(S)); \ 7 TRY_TO(Visit##CLASS(S)); \ 8 return true; \ 9 } \ 10 bool Visit##CLASS(CLASS *S) { return true; } 11 #include "clang/AST/StmtNodes.inc"
3. Traversal
3.1 Type Traversal
3.2 TypeLoc Traversal
3.3 Decl Traversal
The following is the entry for traversing declaration:
template <typename Derived> bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) { if (!D) return true; if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) { if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D)) return TraverseTemplateTypeParamDeclConstraints(TTPD); return true; } switch (D->getKind()) { // Traverse the specific Decl. // ... ...
case Decl::TranslationUnit:
} return true; }
For example, the TraverseObjCInterfaceDecl() method is as the following:
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseObjCInterfaceDecl(ObjCInterfaceDecl *D) { bool ShouldVisitChildren = true; bool ReturnValue = true; if (!getDerived().shouldTraversePostOrder()) do { if (!getDerived().WalkUpFromObjCInterfaceDecl(D)) // Walkup from ObjCInterfaceDecl to Decl. return false; } while (false); { { if (ObjCTypeParamList *typeParamList = D->getTypeParamListAsWritten()) { for (auto typeParam : *typeParamList) { do { if (!getDerived().TraverseObjCTypeParamDecl(typeParam)) return false; } while (false); } } if (TypeSourceInfo *superTInfo = D->getSuperClassTInfo()) { do { if (!getDerived().TraverseTypeLoc(superTInfo->getTypeLoc())) return false; } while (false); } }; } if (ReturnValue && ShouldVisitChildren) do { if (!getDerived().TraverseDeclContextHelper(dyn_cast<DeclContext>(D)))
return false; } while (false); if (ReturnValue) { for (auto *I : D->attrs()) do { if (!getDerived().getDerived().TraverseAttr(I)) return false; } while (false); } if (ReturnValue && getDerived().shouldTraversePostOrder()) do { if (!getDerived().WalkUpFromObjCInterfaceDecl(D)) return false; } while (false); return ReturnValue; }
/**
Visit the Decl, NameDecl, ContainerDecl, ObjCInterface by order.
*/
bool WalkUpFromObjCInterfaceDecl(ObjCInterfaceDecl *D) { do { if (!getDerived().WalkUpFromObjCContainerDecl(D)) // ContainerDecl --> NameDecl --> Decl return false; } while (false); do { if (!getDerived().VisitObjCInterfaceDecl(D)) return false; } while (false); return true; } bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { return true; }
bool WalkUpFromDecl(Decl *D) { return getDerived().VisitDecl(D); } bool VisitDecl(Decl *D) { return true; } bool WalkUpFromNamedDecl(NamedDecl *D) { do { if (!getDerived().WalkUpFromDecl(D)) return false; } while (false); do { if (!getDerived().VisitNamedDecl(D)) return false; } while (false); return true; } bool VisitNamedDecl(NamedDecl *D) { return true; }
3.4 Stmt Traversal
4. Preprocessing this file
The file contains a lot of macros, we will preprocess this file to get the preprocessed file.
// LLVM_PROJECT_PATH is the root path of llvm project.
//
// Using `export LLVM_PROJECT_PATH = ` to declare this env.
clang --trace-includes -D__cplusplus=201402L
-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/
-I${LLVM_PROJECT_PATH}/clang/include/
-I${LLVM_PROJECT_PATH}/.build_RelWithDebInfo/tools/clang/include/
-I${LLVM_PROJECT_PATH}/.build_RelWithDebInfo/include/
-I${LLVM_PROJECT_PATH}/llvm/include/
-E RecursiveASTVisitor.h -o tmp.ii
4.1 Delete the Blank Line
- Open your code in Visual Studio Code.
- From Edit Menu, select Replace or use a short cut key ( command + Option + F on Mac or Ctrl + H on Windows)
- In the find box type ^(\s)*$\n.
- Leave the replace box empty.
- Make sure the 'Use Regular Expression' is selected.
- Select the 'Replace All' button.
4.2 Delete the Linemarker
The linemarker line is like the following:
# 163 "/LLVM/llvm-project/.build_RelWithDebInfo/tools/clang/include/clang/AST/DeclNodes.inc"
To delete the linemarker, as the "4.1 Delete the Blank Line" section, we replace the regex in step 3 with "^#(.)+$\n".
4.3 Delete the header files
Delete the codes from including header files.
The result of preprocessing RecursiveASTVisitor.h using clang -E is at https://gist.github.com/vitonzhangtt/8a1434c22d956bad8cd52259fc7044b0.
Reference
1. clang::RecursiveASTVisitor< Derived > Class Template Reference
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each node.
https://clang.llvm.org/doxygen/classclang_1_1RecursiveASTVisitor.html