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