Home | History | Annotate | Download | only in docs
      1 ==========================================================
      2 How to write RecursiveASTVisitor based ASTFrontendActions.
      3 ==========================================================
      4 
      5 Introduction
      6 ============
      7 
      8 In this tutorial you will learn how to create a FrontendAction that uses
      9 a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified
     10 name.
     11 
     12 Creating a FrontendAction
     13 =========================
     14 
     15 When writing a clang based tool like a Clang Plugin or a standalone tool
     16 based on LibTooling, the common entry point is the FrontendAction.
     17 FrontendAction is an interface that allows execution of user specific
     18 actions as part of the compilation. To run tools over the AST clang
     19 provides the convenience interface ASTFrontendAction, which takes care
     20 of executing the action. The only part left is to implement the
     21 CreateASTConsumer method that returns an ASTConsumer per translation
     22 unit.
     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::unique_ptr<clang::ASTConsumer>(
     31               new FindNamedClassConsumer);
     32         }
     33       };
     34 
     35 Creating an ASTConsumer
     36 =======================
     37 
     38 ASTConsumer is an interface used to write generic actions on an AST,
     39 regardless of how the AST was produced. ASTConsumer provides many
     40 different entry points, but for our use case the only one needed is
     41 HandleTranslationUnit, which is called with the ASTContext for the
     42 translation unit.
     43 
     44 ::
     45 
     46       class FindNamedClassConsumer : public clang::ASTConsumer {
     47       public:
     48         virtual void HandleTranslationUnit(clang::ASTContext &Context) {
     49           // Traversing the translation unit decl via a RecursiveASTVisitor
     50           // will visit all nodes in the AST.
     51           Visitor.TraverseDecl(Context.getTranslationUnitDecl());
     52         }
     53       private:
     54         // A RecursiveASTVisitor implementation.
     55         FindNamedClassVisitor Visitor;
     56       };
     57 
     58 Using the RecursiveASTVisitor
     59 =============================
     60 
     61 Now that everything is hooked up, the next step is to implement a
     62 RecursiveASTVisitor to extract the relevant information from the AST.
     63 
     64 The RecursiveASTVisitor provides hooks of the form bool
     65 VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
     66 nodes, which are passed by-value. We only need to implement the methods
     67 for the relevant node types.
     68 
     69 Let's start by writing a RecursiveASTVisitor that visits all
     70 CXXRecordDecl's.
     71 
     72 ::
     73 
     74       class FindNamedClassVisitor
     75         : public RecursiveASTVisitor<FindNamedClassVisitor> {
     76       public:
     77         bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
     78           // For debugging, dumping the AST nodes will show which nodes are already
     79           // being visited.
     80           Declaration->dump();
     81 
     82           // The return value indicates whether we want the visitation to proceed.
     83           // Return false to stop the traversal of the AST.
     84           return true;
     85         }
     86       };
     87 
     88 In the methods of our RecursiveASTVisitor we can now use the full power
     89 of the Clang AST to drill through to the parts that are interesting for
     90 us. For example, to find all class declaration with a certain name, we
     91 can check for a specific qualified name:
     92 
     93 ::
     94 
     95       bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
     96         if (Declaration->getQualifiedNameAsString() == "n::m::C")
     97           Declaration->dump();
     98         return true;
     99       }
    100 
    101 Accessing the SourceManager and ASTContext
    102 ==========================================
    103 
    104 Some of the information about the AST, like source locations and global
    105 identifier information, are not stored in the AST nodes themselves, but
    106 in the ASTContext and its associated source manager. To retrieve them we
    107 need to hand the ASTContext into our RecursiveASTVisitor implementation.
    108 
    109 The ASTContext is available from the CompilerInstance during the call to
    110 CreateASTConsumer. We can thus extract it there and hand it into our
    111 freshly created FindNamedClassConsumer:
    112 
    113 ::
    114 
    115       virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
    116         clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    117         return std::unique_ptr<clang::ASTConsumer>(
    118             new FindNamedClassConsumer(&Compiler.getASTContext()));
    119       }
    120 
    121 Now that the ASTContext is available in the RecursiveASTVisitor, we can
    122 do more interesting things with AST nodes, like looking up their source
    123 locations:
    124 
    125 ::
    126 
    127       bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
    128         if (Declaration->getQualifiedNameAsString() == "n::m::C") {
    129           // getFullLoc uses the ASTContext's SourceManager to resolve the source
    130           // location and break it up into its line and column parts.
    131           FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
    132           if (FullLocation.isValid())
    133             llvm::outs() << "Found declaration at "
    134                          << FullLocation.getSpellingLineNumber() << ":"
    135                          << FullLocation.getSpellingColumnNumber() << "\n";
    136         }
    137         return true;
    138       }
    139 
    140 Putting it all together
    141 =======================
    142 
    143 Now we can combine all of the above into a small example program:
    144 
    145 ::
    146 
    147       #include "clang/AST/ASTConsumer.h"
    148       #include "clang/AST/RecursiveASTVisitor.h"
    149       #include "clang/Frontend/CompilerInstance.h"
    150       #include "clang/Frontend/FrontendAction.h"
    151       #include "clang/Tooling/Tooling.h"
    152 
    153       using namespace clang;
    154 
    155       class FindNamedClassVisitor
    156         : public RecursiveASTVisitor<FindNamedClassVisitor> {
    157       public:
    158         explicit FindNamedClassVisitor(ASTContext *Context)
    159           : Context(Context) {}
    160 
    161         bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
    162           if (Declaration->getQualifiedNameAsString() == "n::m::C") {
    163             FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
    164             if (FullLocation.isValid())
    165               llvm::outs() << "Found declaration at "
    166                            << FullLocation.getSpellingLineNumber() << ":"
    167                            << FullLocation.getSpellingColumnNumber() << "\n";
    168           }
    169           return true;
    170         }
    171 
    172       private:
    173         ASTContext *Context;
    174       };
    175 
    176       class FindNamedClassConsumer : public clang::ASTConsumer {
    177       public:
    178         explicit FindNamedClassConsumer(ASTContext *Context)
    179           : Visitor(Context) {}
    180 
    181         virtual void HandleTranslationUnit(clang::ASTContext &Context) {
    182           Visitor.TraverseDecl(Context.getTranslationUnitDecl());
    183         }
    184       private:
    185         FindNamedClassVisitor Visitor;
    186       };
    187 
    188       class FindNamedClassAction : public clang::ASTFrontendAction {
    189       public:
    190         virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
    191           clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    192           return std::unique_ptr<clang::ASTConsumer>(
    193               new FindNamedClassConsumer(&Compiler.getASTContext()));
    194         }
    195       };
    196 
    197       int main(int argc, char **argv) {
    198         if (argc > 1) {
    199           clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
    200         }
    201       }
    202 
    203 We store this into a file called FindClassDecls.cpp and create the
    204 following CMakeLists.txt to link it:
    205 
    206 ::
    207 
    208     add_clang_executable(find-class-decls FindClassDecls.cpp)
    209 
    210     target_link_libraries(find-class-decls clangTooling)
    211 
    212 When running this tool over a small code snippet it will output all
    213 declarations of a class n::m::C it found:
    214 
    215 ::
    216 
    217       $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
    218       Found declaration at 1:29
    219 
    220