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 21In this tutorial you will learn how to create a FrontendAction that uses 22a 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 29based on LibTooling, the common entry point is the FrontendAction. 30FrontendAction is an interface that allows execution of user specific actions 31as part of the compilation. To run tools over the AST clang provides the 32convenience interface ASTFrontendAction, which takes care of executing the 33action. The only part left is to implement the CreateASTConsumer method that 34returns 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, 50regardless of how the AST was produced. ASTConsumer provides many different 51entry points, but for our use case the only one needed is HandleTranslationUnit, 52which 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 72RecursiveASTVisitor to extract the relevant information from the AST.</p> 73<p>The RecursiveASTVisitor provides hooks of the form 74bool VisitNodeType(NodeType *) for most AST nodes; the exception are TypeLoc 75nodes, which are passed by-value. We only need to implement the methods for the 76relevant 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 96the Clang AST to drill through to the parts that are interesting for us. For 97example, to find all class declaration with a certain name, we can check for a 98specific 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 113identifier information, are not stored in the AST nodes themselves, but in 114the ASTContext and its associated source manager. To retrieve them we need to 115hand the ASTContext into our RecursiveASTVisitor implementation.</p> 116<p>The ASTContext is available from the CompilerInstance during the call 117to CreateASTConsumer. We can thus extract it there and hand it into our 118freshly 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 127more interesting things with AST nodes, like looking up their source 128locations:</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 207CMakeLists.txt to link it:</p> 208<pre> 209set(LLVM_USED_LIBS clangTooling) 210 211add_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 215declarations 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