| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
| "http://www.w3.org/TR/html4/strict.dtd"> |
| <html> |
| <head> |
| <title>How to write RecursiveASTVisitor based ASTFrontendActions.</title> |
| <link type="text/css" rel="stylesheet" href="../menu.css"> |
| <link type="text/css" rel="stylesheet" href="../content.css"> |
| </head> |
| <body> |
| <div id="content"> |
| |
| <h1>How to write RecursiveASTVisitor based ASTFrontendActions.</h1> |
| |
| <!-- ======================================================================= --> |
| <h2 id="intro">Introduction</h2> |
| <!-- ======================================================================= --> |
| |
| In this tutorial you will learn how to create a FrontendAction that uses |
| a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified name. |
| |
| <!-- ======================================================================= --> |
| <h2 id="action">Creating a FrontendAction</h2> |
| <!-- ======================================================================= --> |
| |
| <p>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.</p> |
| <pre> |
| class FindNamedClassAction : public clang::ASTFrontendAction { |
| public: |
| virtual clang::ASTConsumer *CreateASTConsumer( |
| clang::CompilerInstance &Compiler, llvm::StringRef InFile) { |
| return new FindNamedClassConsumer; |
| } |
| }; |
| </pre> |
| |
| <!-- ======================================================================= --> |
| <h2 id="consumer">Creating an ASTConsumer</h2> |
| <!-- ======================================================================= --> |
| |
| <p>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.</p> |
| <pre> |
| 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; |
| }; |
| </pre> |
| |
| <!-- ======================================================================= --> |
| <h2 id="rav">Using the RecursiveASTVisitor</h2> |
| <!-- ======================================================================= --> |
| |
| <p>Now that everything is hooked up, the next step is to implement a |
| RecursiveASTVisitor to extract the relevant information from the AST.</p> |
| <p>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. |
| </p> |
| <p>Let's start by writing a RecursiveASTVisitor that visits all CXXRecordDecl's. |
| <pre> |
| 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; |
| } |
| }; |
| </pre> |
| </p> |
| <p>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: |
| <pre> |
| bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { |
| if (Declaration->getQualifiedNameAsString() == "n::m::C") |
| Declaration->dump(); |
| return true; |
| } |
| </pre> |
| </p> |
| |
| <!-- ======================================================================= --> |
| <h2 id="context">Accessing the SourceManager and ASTContext</h2> |
| <!-- ======================================================================= --> |
| |
| <p>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.</p> |
| <p>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:</p> |
| <pre> |
| virtual clang::ASTConsumer *CreateASTConsumer( |
| clang::CompilerInstance &Compiler, llvm::StringRef InFile) { |
| return new FindNamedClassConsumer(<b>&Compiler.getASTContext()</b>); |
| } |
| </pre> |
| |
| <p>Now that the ASTContext is available in the RecursiveASTVisitor, we can do |
| more interesting things with AST nodes, like looking up their source |
| locations:</p> |
| <pre> |
| 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; |
| } |
| </pre> |
| |
| <!-- ======================================================================= --> |
| <h2 id="full">Putting it all together</h2> |
| <!-- ======================================================================= --> |
| |
| <p>Now we can combine all of the above into a small example program:</p> |
| <pre> |
| #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 clang::ASTConsumer *CreateASTConsumer( |
| clang::CompilerInstance &Compiler, llvm::StringRef InFile) { |
| return new FindNamedClassConsumer(&Compiler.getASTContext()); |
| } |
| }; |
| |
| int main(int argc, char **argv) { |
| if (argc > 1) { |
| clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]); |
| } |
| } |
| </pre> |
| |
| <p>We store this into a file called FindClassDecls.cpp and create the following |
| CMakeLists.txt to link it:</p> |
| <pre> |
| set(LLVM_USED_LIBS clangTooling) |
| |
| add_clang_executable(find-class-decls FindClassDecls.cpp) |
| </pre> |
| |
| <p>When running this tool over a small code snippet it will output all |
| declarations of a class n::m::C it found:</p> |
| <pre> |
| $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }" |
| Found declaration at 1:29 |
| </pre> |
| |
| </div> |
| </body> |
| </html> |
| |