brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.5 KiB · 8db141f Raw
227 lines · plain
1==========================================================2How to write RecursiveASTVisitor based ASTFrontendActions.3==========================================================4 5Introduction6============7 8In this tutorial you will learn how to create a FrontendAction that uses9a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified10name.11 12Creating a FrontendAction13=========================14 15When writing a clang based tool like a Clang Plugin or a standalone tool16based on LibTooling, the common entry point is the FrontendAction.17FrontendAction is an interface that allows execution of user specific18actions as part of the compilation. To run tools over the AST clang19provides the convenience interface ASTFrontendAction, which takes care20of executing the action. The only part left is to implement the21CreateASTConsumer method that returns an ASTConsumer per translation22unit.23 24::25 26      class FindNamedClassAction : public clang::ASTFrontendAction {27      public:28        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(29          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {30          return std::make_unique<FindNamedClassConsumer>();31        }32      };33 34Creating an ASTConsumer35=======================36 37ASTConsumer is an interface used to write generic actions on an AST,38regardless of how the AST was produced. ASTConsumer provides many39different entry points, but for our use case the only one needed is40HandleTranslationUnit, which is called with the ASTContext for the41translation unit.42 43::44 45      class FindNamedClassConsumer : public clang::ASTConsumer {46      public:47        virtual void HandleTranslationUnit(clang::ASTContext &Context) {48          // Traversing the translation unit decl via a RecursiveASTVisitor49          // will visit all nodes in the AST.50          Visitor.TraverseDecl(Context.getTranslationUnitDecl());51        }52      private:53        // A RecursiveASTVisitor implementation.54        FindNamedClassVisitor Visitor;55      };56 57Using the RecursiveASTVisitor58=============================59 60Now that everything is hooked up, the next step is to implement a61RecursiveASTVisitor to extract the relevant information from the AST.62 63The RecursiveASTVisitor provides hooks of the form bool64VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc65nodes, which are passed by-value. We only need to implement the methods66for the relevant node types.67 68Let's start by writing a RecursiveASTVisitor that visits all69CXXRecordDecl's.70 71::72 73      class FindNamedClassVisitor74        : public RecursiveASTVisitor<FindNamedClassVisitor> {75      public:76        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {77          // For debugging, dumping the AST nodes will show which nodes are already78          // being visited.79          Declaration->dump();80 81          // The return value indicates whether we want the visitation to proceed.82          // Return false to stop the traversal of the AST.83          return true;84        }85      };86 87In the methods of our RecursiveASTVisitor we can now use the full power88of the Clang AST to drill through to the parts that are interesting for89us. For example, to find all class declaration with a certain name, we90can check for a specific qualified name:91 92::93 94      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {95        if (Declaration->getQualifiedNameAsString() == "n::m::C")96          Declaration->dump();97        return true;98      }99 100Accessing the SourceManager and ASTContext101==========================================102 103Some of the information about the AST, like source locations and global104identifier information, is not stored in the AST nodes themselves, but105in the ASTContext and its associated source manager. To retrieve them we106need to hand the ASTContext into our RecursiveASTVisitor implementation.107 108The ASTContext is available from the CompilerInstance during the call to109CreateASTConsumer. We can thus extract it there and hand it into our110freshly created FindNamedClassConsumer:111 112::113 114      virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(115        clang::CompilerInstance &Compiler, llvm::StringRef InFile) {116        return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());117      }118 119Now that the ASTContext is available in the RecursiveASTVisitor, we can120do more interesting things with AST nodes, like looking up their source121locations:122 123::124 125      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {126        if (Declaration->getQualifiedNameAsString() == "n::m::C") {127          // getFullLoc uses the ASTContext's SourceManager to resolve the source128          // location and break it up into its line and column parts.129          FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());130          if (FullLocation.isValid())131            llvm::outs() << "Found declaration at "132                         << FullLocation.getSpellingLineNumber() << ":"133                         << FullLocation.getSpellingColumnNumber() << "\n";134        }135        return true;136      }137 138Putting it all together139=======================140 141Now we can combine all of the above into a small example program:142 143::144 145      #include "clang/AST/ASTConsumer.h"146      #include "clang/AST/RecursiveASTVisitor.h"147      #include "clang/Frontend/CompilerInstance.h"148      #include "clang/Frontend/FrontendAction.h"149      #include "clang/Tooling/Tooling.h"150 151      using namespace clang;152 153      class FindNamedClassVisitor154        : public RecursiveASTVisitor<FindNamedClassVisitor> {155      public:156        explicit FindNamedClassVisitor(ASTContext *Context)157          : Context(Context) {}158 159        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {160          if (Declaration->getQualifiedNameAsString() == "n::m::C") {161            FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());162            if (FullLocation.isValid())163              llvm::outs() << "Found declaration at "164                           << FullLocation.getSpellingLineNumber() << ":"165                           << FullLocation.getSpellingColumnNumber() << "\n";166          }167          return true;168        }169 170      private:171        ASTContext *Context;172      };173 174      class FindNamedClassConsumer : public clang::ASTConsumer {175      public:176        explicit FindNamedClassConsumer(ASTContext *Context)177          : Visitor(Context) {}178 179        virtual void HandleTranslationUnit(clang::ASTContext &Context) {180          Visitor.TraverseDecl(Context.getTranslationUnitDecl());181        }182      private:183        FindNamedClassVisitor Visitor;184      };185 186      class FindNamedClassAction : public clang::ASTFrontendAction {187      public:188        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(189          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {190          return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());191        }192      };193 194      int main(int argc, char **argv) {195        if (argc > 1) {196          clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]);197        }198      }199 200We store this into a file called FindClassDecls.cpp and create the201following CMakeLists.txt to link it:202 203::204 205    set(LLVM_LINK_COMPONENTS206      Support207      )208 209    add_clang_executable(find-class-decls FindClassDecls.cpp)210 211    target_link_libraries(find-class-decls212      PRIVATE213      clangAST214      clangBasic215      clangFrontend216      clangSerialization217      clangTooling218      )219 220When running this tool over a small code snippet it will output all221declarations of a class n::m::C it found:222 223::224 225      $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"226      Found declaration at 1:29227