|  | ========================================================== | 
|  | How to write RecursiveASTVisitor based ASTFrontendActions. | 
|  | ========================================================== | 
|  |  | 
|  | Introduction | 
|  | ============ | 
|  |  | 
|  | In this tutorial you will learn how to create a FrontendAction that uses | 
|  | a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified | 
|  | name. | 
|  |  | 
|  | Creating a FrontendAction | 
|  | ========================= | 
|  |  | 
|  | When writing a clang based tool like a Clang Plugin or a standalone tool | 
|  | based on LibTooling, the common entry point is the FrontendAction. | 
|  | FrontendAction is an interface that allows execution of user specific | 
|  | actions as part of the compilation. To run tools over the AST clang | 
|  | provides the convenience interface ASTFrontendAction, which takes care | 
|  | of executing the action. The only part left is to implement the | 
|  | CreateASTConsumer method that returns an ASTConsumer per translation | 
|  | unit. | 
|  |  | 
|  | :: | 
|  |  | 
|  | class FindNamedClassAction : public clang::ASTFrontendAction { | 
|  | public: | 
|  | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
|  | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
|  | return std::unique_ptr<clang::ASTConsumer>( | 
|  | new FindNamedClassConsumer); | 
|  | } | 
|  | }; | 
|  |  | 
|  | Creating an ASTConsumer | 
|  | ======================= | 
|  |  | 
|  | ASTConsumer is an interface used to write generic actions on an AST, | 
|  | regardless of how the AST was produced. ASTConsumer provides many | 
|  | different entry points, but for our use case the only one needed is | 
|  | HandleTranslationUnit, which is called with the ASTContext for the | 
|  | translation unit. | 
|  |  | 
|  | :: | 
|  |  | 
|  | class FindNamedClassConsumer : public clang::ASTConsumer { | 
|  | public: | 
|  | virtual void HandleTranslationUnit(clang::ASTContext &Context) { | 
|  | // Traversing the translation unit decl via a RecursiveASTVisitor | 
|  | // will visit all nodes in the AST. | 
|  | Visitor.TraverseDecl(Context.getTranslationUnitDecl()); | 
|  | } | 
|  | private: | 
|  | // A RecursiveASTVisitor implementation. | 
|  | FindNamedClassVisitor Visitor; | 
|  | }; | 
|  |  | 
|  | Using the RecursiveASTVisitor | 
|  | ============================= | 
|  |  | 
|  | Now that everything is hooked up, the next step is to implement a | 
|  | RecursiveASTVisitor to extract the relevant information from the AST. | 
|  |  | 
|  | The RecursiveASTVisitor provides hooks of the form bool | 
|  | VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc | 
|  | nodes, which are passed by-value. We only need to implement the methods | 
|  | for the relevant node types. | 
|  |  | 
|  | Let's start by writing a RecursiveASTVisitor that visits all | 
|  | CXXRecordDecl's. | 
|  |  | 
|  | :: | 
|  |  | 
|  | class FindNamedClassVisitor | 
|  | : public RecursiveASTVisitor<FindNamedClassVisitor> { | 
|  | public: | 
|  | bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { | 
|  | // For debugging, dumping the AST nodes will show which nodes are already | 
|  | // being visited. | 
|  | Declaration->dump(); | 
|  |  | 
|  | // The return value indicates whether we want the visitation to proceed. | 
|  | // Return false to stop the traversal of the AST. | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | In the methods of our RecursiveASTVisitor we can now use the full power | 
|  | of the Clang AST to drill through to the parts that are interesting for | 
|  | us. For example, to find all class declaration with a certain name, we | 
|  | can check for a specific qualified name: | 
|  |  | 
|  | :: | 
|  |  | 
|  | bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { | 
|  | if (Declaration->getQualifiedNameAsString() == "n::m::C") | 
|  | Declaration->dump(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | Accessing the SourceManager and ASTContext | 
|  | ========================================== | 
|  |  | 
|  | Some of the information about the AST, like source locations and global | 
|  | identifier information, are not stored in the AST nodes themselves, but | 
|  | in the ASTContext and its associated source manager. To retrieve them we | 
|  | need to hand the ASTContext into our RecursiveASTVisitor implementation. | 
|  |  | 
|  | The ASTContext is available from the CompilerInstance during the call to | 
|  | CreateASTConsumer. We can thus extract it there and hand it into our | 
|  | freshly created FindNamedClassConsumer: | 
|  |  | 
|  | :: | 
|  |  | 
|  | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
|  | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
|  | return std::unique_ptr<clang::ASTConsumer>( | 
|  | new FindNamedClassConsumer(&Compiler.getASTContext())); | 
|  | } | 
|  |  | 
|  | Now that the ASTContext is available in the RecursiveASTVisitor, we can | 
|  | do more interesting things with AST nodes, like looking up their source | 
|  | locations: | 
|  |  | 
|  | :: | 
|  |  | 
|  | bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { | 
|  | if (Declaration->getQualifiedNameAsString() == "n::m::C") { | 
|  | // getFullLoc uses the ASTContext's SourceManager to resolve the source | 
|  | // location and break it up into its line and column parts. | 
|  | FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); | 
|  | if (FullLocation.isValid()) | 
|  | llvm::outs() << "Found declaration at " | 
|  | << FullLocation.getSpellingLineNumber() << ":" | 
|  | << FullLocation.getSpellingColumnNumber() << "\n"; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | Putting it all together | 
|  | ======================= | 
|  |  | 
|  | Now we can combine all of the above into a small example program: | 
|  |  | 
|  | :: | 
|  |  | 
|  | #include "clang/AST/ASTConsumer.h" | 
|  | #include "clang/AST/RecursiveASTVisitor.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Frontend/FrontendAction.h" | 
|  | #include "clang/Tooling/Tooling.h" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | class FindNamedClassVisitor | 
|  | : public RecursiveASTVisitor<FindNamedClassVisitor> { | 
|  | public: | 
|  | explicit FindNamedClassVisitor(ASTContext *Context) | 
|  | : Context(Context) {} | 
|  |  | 
|  | bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { | 
|  | if (Declaration->getQualifiedNameAsString() == "n::m::C") { | 
|  | FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); | 
|  | if (FullLocation.isValid()) | 
|  | llvm::outs() << "Found declaration at " | 
|  | << FullLocation.getSpellingLineNumber() << ":" | 
|  | << FullLocation.getSpellingColumnNumber() << "\n"; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ASTContext *Context; | 
|  | }; | 
|  |  | 
|  | class FindNamedClassConsumer : public clang::ASTConsumer { | 
|  | public: | 
|  | explicit FindNamedClassConsumer(ASTContext *Context) | 
|  | : Visitor(Context) {} | 
|  |  | 
|  | virtual void HandleTranslationUnit(clang::ASTContext &Context) { | 
|  | Visitor.TraverseDecl(Context.getTranslationUnitDecl()); | 
|  | } | 
|  | private: | 
|  | FindNamedClassVisitor Visitor; | 
|  | }; | 
|  |  | 
|  | class FindNamedClassAction : public clang::ASTFrontendAction { | 
|  | public: | 
|  | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
|  | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
|  | return std::unique_ptr<clang::ASTConsumer>( | 
|  | new FindNamedClassConsumer(&Compiler.getASTContext())); | 
|  | } | 
|  | }; | 
|  |  | 
|  | int main(int argc, char **argv) { | 
|  | if (argc > 1) { | 
|  | clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | We store this into a file called FindClassDecls.cpp and create the | 
|  | following CMakeLists.txt to link it: | 
|  |  | 
|  | :: | 
|  |  | 
|  | add_clang_executable(find-class-decls FindClassDecls.cpp) | 
|  |  | 
|  | target_link_libraries(find-class-decls clangTooling) | 
|  |  | 
|  | When running this tool over a small code snippet it will output all | 
|  | declarations of a class n::m::C it found: | 
|  |  | 
|  | :: | 
|  |  | 
|  | $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }" | 
|  | Found declaration at 1:29 | 
|  |  |