1========================================================== 2How to write RecursiveASTVisitor based ASTFrontendActions. 3========================================================== 4 5Introduction 6============ 7 8In this tutorial you will learn how to create a FrontendAction that uses 9a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified 10name. 11 12Creating a FrontendAction 13========================= 14 15When writing a clang based tool like a Clang Plugin or a standalone tool 16based on LibTooling, the common entry point is the FrontendAction. 17FrontendAction is an interface that allows execution of user specific 18actions as part of the compilation. To run tools over the AST clang 19provides the convenience interface ASTFrontendAction, which takes care 20of executing the action. The only part left is to implement the 21CreateASTConsumer method that returns an ASTConsumer per translation 22unit. 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::make_unique<FindNamedClassConsumer>(); 31 } 32 }; 33 34Creating an ASTConsumer 35======================= 36 37ASTConsumer is an interface used to write generic actions on an AST, 38regardless of how the AST was produced. ASTConsumer provides many 39different entry points, but for our use case the only one needed is 40HandleTranslationUnit, which is called with the ASTContext for the 41translation 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 57Using the RecursiveASTVisitor 58============================= 59 60Now that everything is hooked up, the next step is to implement a 61RecursiveASTVisitor to extract the relevant information from the AST. 62 63The RecursiveASTVisitor provides hooks of the form bool 64VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc 65nodes, which are passed by-value. We only need to implement the methods 66for the relevant node types. 67 68Let's start by writing a RecursiveASTVisitor that visits all 69CXXRecordDecl'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 87In the methods of our RecursiveASTVisitor we can now use the full power 88of the Clang AST to drill through to the parts that are interesting for 89us. For example, to find all class declaration with a certain name, we 90can 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 100Accessing the SourceManager and ASTContext 101========================================== 102 103Some of the information about the AST, like source locations and global 104identifier information, are not stored in the AST nodes themselves, but 105in the ASTContext and its associated source manager. To retrieve them we 106need to hand the ASTContext into our RecursiveASTVisitor implementation. 107 108The ASTContext is available from the CompilerInstance during the call to 109CreateASTConsumer. We can thus extract it there and hand it into our 110freshly created FindNamedClassConsumer: 111 112:: 113 114 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 115 clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 116 return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext()); 117 } 118 119Now that the ASTContext is available in the RecursiveASTVisitor, we can 120do more interesting things with AST nodes, like looking up their source 121locations: 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->getBeginLoc()); 130 if (FullLocation.isValid()) 131 llvm::outs() << "Found declaration at " 132 << FullLocation.getSpellingLineNumber() << ":" 133 << FullLocation.getSpellingColumnNumber() << "\n"; 134 } 135 return true; 136 } 137 138Putting it all together 139======================= 140 141Now 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->getBeginLoc()); 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 std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 189 clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 190 return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext()); 191 } 192 }; 193 194 int main(int argc, char **argv) { 195 if (argc > 1) { 196 clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]); 197 } 198 } 199 200We store this into a file called FindClassDecls.cpp and create the 201following CMakeLists.txt to link it: 202 203:: 204 205 set(LLVM_LINK_COMPONENTS 206 Support 207 ) 208 209 add_clang_executable(find-class-decls FindClassDecls.cpp) 210 211 target_link_libraries(find-class-decls 212 PRIVATE 213 clangAST 214 clangBasic 215 clangFrontend 216 clangSerialization 217 clangTooling 218 ) 219 220When running this tool over a small code snippet it will output all 221declarations of a class n::m::C it found: 222 223:: 224 225 $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }" 226 Found declaration at 1:29 227 228