diff options
Diffstat (limited to 'docs/RAVFrontendAction.rst')
-rw-r--r-- | docs/RAVFrontendAction.rst | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/docs/RAVFrontendAction.rst b/docs/RAVFrontendAction.rst new file mode 100644 index 0000000..2f60ce9 --- /dev/null +++ b/docs/RAVFrontendAction.rst @@ -0,0 +1,216 @@ +========================================================== +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 clang::ASTConsumer *CreateASTConsumer( + clang::CompilerInstance &Compiler, llvm::StringRef InFile) { + return 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 clang::ASTConsumer *CreateASTConsumer( + clang::CompilerInstance &Compiler, llvm::StringRef InFile) { + return 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 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]); + } + } + +We store this into a file called FindClassDecls.cpp and create the +following CMakeLists.txt to link it: + +:: + + set(LLVM_USED_LIBS clangTooling) + + add_clang_executable(find-class-decls FindClassDecls.cpp) + +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 + |