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 &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 &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<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 &Compiler, llvm::StringRef InFile) { 122 return new FindNamedClassConsumer(<b>&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() << "Found declaration at " 137 << FullLocation.getSpellingLineNumber() << ":" 138 << FullLocation.getSpellingColumnNumber() << "\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<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() << "Found declaration at " 169 << FullLocation.getSpellingLineNumber() << ":" 170 << FullLocation.getSpellingColumnNumber() << "\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 &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 &Compiler, llvm::StringRef InFile) { 195 return new FindNamedClassConsumer(&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