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