1*67e74705SXin Li========================================================== 2*67e74705SXin LiHow to write RecursiveASTVisitor based ASTFrontendActions. 3*67e74705SXin Li========================================================== 4*67e74705SXin Li 5*67e74705SXin LiIntroduction 6*67e74705SXin Li============ 7*67e74705SXin Li 8*67e74705SXin LiIn this tutorial you will learn how to create a FrontendAction that uses 9*67e74705SXin Lia RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified 10*67e74705SXin Liname. 11*67e74705SXin Li 12*67e74705SXin LiCreating a FrontendAction 13*67e74705SXin Li========================= 14*67e74705SXin Li 15*67e74705SXin LiWhen writing a clang based tool like a Clang Plugin or a standalone tool 16*67e74705SXin Libased on LibTooling, the common entry point is the FrontendAction. 17*67e74705SXin LiFrontendAction is an interface that allows execution of user specific 18*67e74705SXin Liactions as part of the compilation. To run tools over the AST clang 19*67e74705SXin Liprovides the convenience interface ASTFrontendAction, which takes care 20*67e74705SXin Liof executing the action. The only part left is to implement the 21*67e74705SXin LiCreateASTConsumer method that returns an ASTConsumer per translation 22*67e74705SXin Liunit. 23*67e74705SXin Li 24*67e74705SXin Li:: 25*67e74705SXin Li 26*67e74705SXin Li class FindNamedClassAction : public clang::ASTFrontendAction { 27*67e74705SXin Li public: 28*67e74705SXin Li virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 29*67e74705SXin Li clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 30*67e74705SXin Li return std::unique_ptr<clang::ASTConsumer>( 31*67e74705SXin Li new FindNamedClassConsumer); 32*67e74705SXin Li } 33*67e74705SXin Li }; 34*67e74705SXin Li 35*67e74705SXin LiCreating an ASTConsumer 36*67e74705SXin Li======================= 37*67e74705SXin Li 38*67e74705SXin LiASTConsumer is an interface used to write generic actions on an AST, 39*67e74705SXin Liregardless of how the AST was produced. ASTConsumer provides many 40*67e74705SXin Lidifferent entry points, but for our use case the only one needed is 41*67e74705SXin LiHandleTranslationUnit, which is called with the ASTContext for the 42*67e74705SXin Litranslation unit. 43*67e74705SXin Li 44*67e74705SXin Li:: 45*67e74705SXin Li 46*67e74705SXin Li class FindNamedClassConsumer : public clang::ASTConsumer { 47*67e74705SXin Li public: 48*67e74705SXin Li virtual void HandleTranslationUnit(clang::ASTContext &Context) { 49*67e74705SXin Li // Traversing the translation unit decl via a RecursiveASTVisitor 50*67e74705SXin Li // will visit all nodes in the AST. 51*67e74705SXin Li Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 52*67e74705SXin Li } 53*67e74705SXin Li private: 54*67e74705SXin Li // A RecursiveASTVisitor implementation. 55*67e74705SXin Li FindNamedClassVisitor Visitor; 56*67e74705SXin Li }; 57*67e74705SXin Li 58*67e74705SXin LiUsing the RecursiveASTVisitor 59*67e74705SXin Li============================= 60*67e74705SXin Li 61*67e74705SXin LiNow that everything is hooked up, the next step is to implement a 62*67e74705SXin LiRecursiveASTVisitor to extract the relevant information from the AST. 63*67e74705SXin Li 64*67e74705SXin LiThe RecursiveASTVisitor provides hooks of the form bool 65*67e74705SXin LiVisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc 66*67e74705SXin Linodes, which are passed by-value. We only need to implement the methods 67*67e74705SXin Lifor the relevant node types. 68*67e74705SXin Li 69*67e74705SXin LiLet's start by writing a RecursiveASTVisitor that visits all 70*67e74705SXin LiCXXRecordDecl's. 71*67e74705SXin Li 72*67e74705SXin Li:: 73*67e74705SXin Li 74*67e74705SXin Li class FindNamedClassVisitor 75*67e74705SXin Li : public RecursiveASTVisitor<FindNamedClassVisitor> { 76*67e74705SXin Li public: 77*67e74705SXin Li bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 78*67e74705SXin Li // For debugging, dumping the AST nodes will show which nodes are already 79*67e74705SXin Li // being visited. 80*67e74705SXin Li Declaration->dump(); 81*67e74705SXin Li 82*67e74705SXin Li // The return value indicates whether we want the visitation to proceed. 83*67e74705SXin Li // Return false to stop the traversal of the AST. 84*67e74705SXin Li return true; 85*67e74705SXin Li } 86*67e74705SXin Li }; 87*67e74705SXin Li 88*67e74705SXin LiIn the methods of our RecursiveASTVisitor we can now use the full power 89*67e74705SXin Liof the Clang AST to drill through to the parts that are interesting for 90*67e74705SXin Lius. For example, to find all class declaration with a certain name, we 91*67e74705SXin Lican check for a specific qualified name: 92*67e74705SXin Li 93*67e74705SXin Li:: 94*67e74705SXin Li 95*67e74705SXin Li bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 96*67e74705SXin Li if (Declaration->getQualifiedNameAsString() == "n::m::C") 97*67e74705SXin Li Declaration->dump(); 98*67e74705SXin Li return true; 99*67e74705SXin Li } 100*67e74705SXin Li 101*67e74705SXin LiAccessing the SourceManager and ASTContext 102*67e74705SXin Li========================================== 103*67e74705SXin Li 104*67e74705SXin LiSome of the information about the AST, like source locations and global 105*67e74705SXin Liidentifier information, are not stored in the AST nodes themselves, but 106*67e74705SXin Liin the ASTContext and its associated source manager. To retrieve them we 107*67e74705SXin Lineed to hand the ASTContext into our RecursiveASTVisitor implementation. 108*67e74705SXin Li 109*67e74705SXin LiThe ASTContext is available from the CompilerInstance during the call to 110*67e74705SXin LiCreateASTConsumer. We can thus extract it there and hand it into our 111*67e74705SXin Lifreshly created FindNamedClassConsumer: 112*67e74705SXin Li 113*67e74705SXin Li:: 114*67e74705SXin Li 115*67e74705SXin Li virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 116*67e74705SXin Li clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 117*67e74705SXin Li return std::unique_ptr<clang::ASTConsumer>( 118*67e74705SXin Li new FindNamedClassConsumer(&Compiler.getASTContext())); 119*67e74705SXin Li } 120*67e74705SXin Li 121*67e74705SXin LiNow that the ASTContext is available in the RecursiveASTVisitor, we can 122*67e74705SXin Lido more interesting things with AST nodes, like looking up their source 123*67e74705SXin Lilocations: 124*67e74705SXin Li 125*67e74705SXin Li:: 126*67e74705SXin Li 127*67e74705SXin Li bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 128*67e74705SXin Li if (Declaration->getQualifiedNameAsString() == "n::m::C") { 129*67e74705SXin Li // getFullLoc uses the ASTContext's SourceManager to resolve the source 130*67e74705SXin Li // location and break it up into its line and column parts. 131*67e74705SXin Li FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); 132*67e74705SXin Li if (FullLocation.isValid()) 133*67e74705SXin Li llvm::outs() << "Found declaration at " 134*67e74705SXin Li << FullLocation.getSpellingLineNumber() << ":" 135*67e74705SXin Li << FullLocation.getSpellingColumnNumber() << "\n"; 136*67e74705SXin Li } 137*67e74705SXin Li return true; 138*67e74705SXin Li } 139*67e74705SXin Li 140*67e74705SXin LiPutting it all together 141*67e74705SXin Li======================= 142*67e74705SXin Li 143*67e74705SXin LiNow we can combine all of the above into a small example program: 144*67e74705SXin Li 145*67e74705SXin Li:: 146*67e74705SXin Li 147*67e74705SXin Li #include "clang/AST/ASTConsumer.h" 148*67e74705SXin Li #include "clang/AST/RecursiveASTVisitor.h" 149*67e74705SXin Li #include "clang/Frontend/CompilerInstance.h" 150*67e74705SXin Li #include "clang/Frontend/FrontendAction.h" 151*67e74705SXin Li #include "clang/Tooling/Tooling.h" 152*67e74705SXin Li 153*67e74705SXin Li using namespace clang; 154*67e74705SXin Li 155*67e74705SXin Li class FindNamedClassVisitor 156*67e74705SXin Li : public RecursiveASTVisitor<FindNamedClassVisitor> { 157*67e74705SXin Li public: 158*67e74705SXin Li explicit FindNamedClassVisitor(ASTContext *Context) 159*67e74705SXin Li : Context(Context) {} 160*67e74705SXin Li 161*67e74705SXin Li bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 162*67e74705SXin Li if (Declaration->getQualifiedNameAsString() == "n::m::C") { 163*67e74705SXin Li FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); 164*67e74705SXin Li if (FullLocation.isValid()) 165*67e74705SXin Li llvm::outs() << "Found declaration at " 166*67e74705SXin Li << FullLocation.getSpellingLineNumber() << ":" 167*67e74705SXin Li << FullLocation.getSpellingColumnNumber() << "\n"; 168*67e74705SXin Li } 169*67e74705SXin Li return true; 170*67e74705SXin Li } 171*67e74705SXin Li 172*67e74705SXin Li private: 173*67e74705SXin Li ASTContext *Context; 174*67e74705SXin Li }; 175*67e74705SXin Li 176*67e74705SXin Li class FindNamedClassConsumer : public clang::ASTConsumer { 177*67e74705SXin Li public: 178*67e74705SXin Li explicit FindNamedClassConsumer(ASTContext *Context) 179*67e74705SXin Li : Visitor(Context) {} 180*67e74705SXin Li 181*67e74705SXin Li virtual void HandleTranslationUnit(clang::ASTContext &Context) { 182*67e74705SXin Li Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 183*67e74705SXin Li } 184*67e74705SXin Li private: 185*67e74705SXin Li FindNamedClassVisitor Visitor; 186*67e74705SXin Li }; 187*67e74705SXin Li 188*67e74705SXin Li class FindNamedClassAction : public clang::ASTFrontendAction { 189*67e74705SXin Li public: 190*67e74705SXin Li virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 191*67e74705SXin Li clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 192*67e74705SXin Li return std::unique_ptr<clang::ASTConsumer>( 193*67e74705SXin Li new FindNamedClassConsumer(&Compiler.getASTContext())); 194*67e74705SXin Li } 195*67e74705SXin Li }; 196*67e74705SXin Li 197*67e74705SXin Li int main(int argc, char **argv) { 198*67e74705SXin Li if (argc > 1) { 199*67e74705SXin Li clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]); 200*67e74705SXin Li } 201*67e74705SXin Li } 202*67e74705SXin Li 203*67e74705SXin LiWe store this into a file called FindClassDecls.cpp and create the 204*67e74705SXin Lifollowing CMakeLists.txt to link it: 205*67e74705SXin Li 206*67e74705SXin Li:: 207*67e74705SXin Li 208*67e74705SXin Li add_clang_executable(find-class-decls FindClassDecls.cpp) 209*67e74705SXin Li 210*67e74705SXin Li target_link_libraries(find-class-decls clangTooling) 211*67e74705SXin Li 212*67e74705SXin LiWhen running this tool over a small code snippet it will output all 213*67e74705SXin Lideclarations of a class n::m::C it found: 214*67e74705SXin Li 215*67e74705SXin Li:: 216*67e74705SXin Li 217*67e74705SXin Li $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }" 218*67e74705SXin Li Found declaration at 1:29 219*67e74705SXin Li 220