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 clang::ASTConsumer *CreateASTConsumer(
     29           clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
     30           return new FindNamedClassConsumer;
     31         }
     32       };
     33 
     34 Creating an ASTConsumer
     35 =======================
     36 
     37 ASTConsumer is an interface used to write generic actions on an AST,
     38 regardless of how the AST was produced. ASTConsumer provides many
     39 different entry points, but for our use case the only one needed is
     40 HandleTranslationUnit, which is called with the ASTContext for the
     41 translation 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 RecursiveASTVisitor
     49           // will visit all nodes in the AST.
     50           Visitor.TraverseDecl(Context.getTranslationUnitDecl());
     51         }
     52       private:
     53         // A RecursiveASTVisitor implementation.
     54         FindNamedClassVisitor Visitor;
     55       };
     56 
     57 Using the RecursiveASTVisitor
     58 =============================
     59 
     60 Now that everything is hooked up, the next step is to implement a
     61 RecursiveASTVisitor to extract the relevant information from the AST.
     62 
     63 The RecursiveASTVisitor provides hooks of the form bool
     64 VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
     65 nodes, which are passed by-value. We only need to implement the methods
     66 for the relevant node types.
     67 
     68 Let's start by writing a RecursiveASTVisitor that visits all
     69 CXXRecordDecl's.
     70 
     71 ::
     72 
     73       class FindNamedClassVisitor
     74         : public RecursiveASTVisitor<FindNamedClassVisitor> {
     75       public:
     76         bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
     77           // For debugging, dumping the AST nodes will show which nodes are already
     78           // 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 
     87 In the methods of our RecursiveASTVisitor we can now use the full power
     88 of the Clang AST to drill through to the parts that are interesting for
     89 us. For example, to find all class declaration with a certain name, we
     90 can 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 
    100 Accessing the SourceManager and ASTContext
    101 ==========================================
    102 
    103 Some of the information about the AST, like source locations and global
    104 identifier information, are not stored in the AST nodes themselves, but
    105 in the ASTContext and its associated source manager. To retrieve them we
    106 need to hand the ASTContext into our RecursiveASTVisitor implementation.
    107 
    108 The ASTContext is available from the CompilerInstance during the call to
    109 CreateASTConsumer. We can thus extract it there and hand it into our
    110 freshly created FindNamedClassConsumer:
    111 
    112 ::
    113 
    114       virtual clang::ASTConsumer *CreateASTConsumer(
    115         clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    116         return new FindNamedClassConsumer(&Compiler.getASTContext());
    117       }
    118 
    119 Now that the ASTContext is available in the RecursiveASTVisitor, we can
    120 do more interesting things with AST nodes, like looking up their source
    121 locations:
    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 source
    128           // location and break it up into its line and column parts.
    129           FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
    130           if (FullLocation.isValid())
    131             llvm::outs() << "Found declaration at "
    132                          << FullLocation.getSpellingLineNumber() << ":"
    133                          << FullLocation.getSpellingColumnNumber() << "\n";
    134         }
    135         return true;
    136       }
    137 
    138 Putting it all together
    139 =======================
    140 
    141 Now 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 FindNamedClassVisitor
    154         : 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->getLocStart());
    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 clang::ASTConsumer *CreateASTConsumer(
    189           clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    190           return new FindNamedClassConsumer(&Compiler.getASTContext());
    191         }
    192       };
    193 
    194       int main(int argc, char **argv) {
    195         if (argc > 1) {
    196           clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
    197         }
    198       }
    199 
    200 We store this into a file called FindClassDecls.cpp and create the
    201 following CMakeLists.txt to link it:
    202 
    203 ::
    204 
    205     set(LLVM_USED_LIBS clangTooling)
    206 
    207     add_clang_executable(find-class-decls FindClassDecls.cpp)
    208 
    209 When running this tool over a small code snippet it will output all
    210 declarations of a class n::m::C it found:
    211 
    212 ::
    213 
    214       $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
    215       Found declaration at 1:29
    216 
    217