123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- ==========================================================
- 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->getBeginLoc());
- 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->getBeginLoc());
- 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(std::make_unique<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
|