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 std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 29 clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 30 return std::unique_ptr<clang::ASTConsumer>( 31 new FindNamedClassConsumer); 32 } 33 }; 34 35 Creating an ASTConsumer 36 ======================= 37 38 ASTConsumer is an interface used to write generic actions on an AST, 39 regardless of how the AST was produced. ASTConsumer provides many 40 different entry points, but for our use case the only one needed is 41 HandleTranslationUnit, which is called with the ASTContext for the 42 translation unit. 43 44 :: 45 46 class FindNamedClassConsumer : public clang::ASTConsumer { 47 public: 48 virtual void HandleTranslationUnit(clang::ASTContext &Context) { 49 // Traversing the translation unit decl via a RecursiveASTVisitor 50 // will visit all nodes in the AST. 51 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 52 } 53 private: 54 // A RecursiveASTVisitor implementation. 55 FindNamedClassVisitor Visitor; 56 }; 57 58 Using the RecursiveASTVisitor 59 ============================= 60 61 Now that everything is hooked up, the next step is to implement a 62 RecursiveASTVisitor to extract the relevant information from the AST. 63 64 The RecursiveASTVisitor provides hooks of the form bool 65 VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc 66 nodes, which are passed by-value. We only need to implement the methods 67 for the relevant node types. 68 69 Let's start by writing a RecursiveASTVisitor that visits all 70 CXXRecordDecl's. 71 72 :: 73 74 class FindNamedClassVisitor 75 : public RecursiveASTVisitor<FindNamedClassVisitor> { 76 public: 77 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 78 // For debugging, dumping the AST nodes will show which nodes are already 79 // being visited. 80 Declaration->dump(); 81 82 // The return value indicates whether we want the visitation to proceed. 83 // Return false to stop the traversal of the AST. 84 return true; 85 } 86 }; 87 88 In the methods of our RecursiveASTVisitor we can now use the full power 89 of the Clang AST to drill through to the parts that are interesting for 90 us. For example, to find all class declaration with a certain name, we 91 can check for a specific qualified name: 92 93 :: 94 95 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 96 if (Declaration->getQualifiedNameAsString() == "n::m::C") 97 Declaration->dump(); 98 return true; 99 } 100 101 Accessing the SourceManager and ASTContext 102 ========================================== 103 104 Some of the information about the AST, like source locations and global 105 identifier information, are not stored in the AST nodes themselves, but 106 in the ASTContext and its associated source manager. To retrieve them we 107 need to hand the ASTContext into our RecursiveASTVisitor implementation. 108 109 The ASTContext is available from the CompilerInstance during the call to 110 CreateASTConsumer. We can thus extract it there and hand it into our 111 freshly created FindNamedClassConsumer: 112 113 :: 114 115 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 116 clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 117 return std::unique_ptr<clang::ASTConsumer>( 118 new FindNamedClassConsumer(&Compiler.getASTContext())); 119 } 120 121 Now that the ASTContext is available in the RecursiveASTVisitor, we can 122 do more interesting things with AST nodes, like looking up their source 123 locations: 124 125 :: 126 127 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 128 if (Declaration->getQualifiedNameAsString() == "n::m::C") { 129 // getFullLoc uses the ASTContext's SourceManager to resolve the source 130 // location and break it up into its line and column parts. 131 FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); 132 if (FullLocation.isValid()) 133 llvm::outs() << "Found declaration at " 134 << FullLocation.getSpellingLineNumber() << ":" 135 << FullLocation.getSpellingColumnNumber() << "\n"; 136 } 137 return true; 138 } 139 140 Putting it all together 141 ======================= 142 143 Now we can combine all of the above into a small example program: 144 145 :: 146 147 #include "clang/AST/ASTConsumer.h" 148 #include "clang/AST/RecursiveASTVisitor.h" 149 #include "clang/Frontend/CompilerInstance.h" 150 #include "clang/Frontend/FrontendAction.h" 151 #include "clang/Tooling/Tooling.h" 152 153 using namespace clang; 154 155 class FindNamedClassVisitor 156 : public RecursiveASTVisitor<FindNamedClassVisitor> { 157 public: 158 explicit FindNamedClassVisitor(ASTContext *Context) 159 : Context(Context) {} 160 161 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) { 162 if (Declaration->getQualifiedNameAsString() == "n::m::C") { 163 FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart()); 164 if (FullLocation.isValid()) 165 llvm::outs() << "Found declaration at " 166 << FullLocation.getSpellingLineNumber() << ":" 167 << FullLocation.getSpellingColumnNumber() << "\n"; 168 } 169 return true; 170 } 171 172 private: 173 ASTContext *Context; 174 }; 175 176 class FindNamedClassConsumer : public clang::ASTConsumer { 177 public: 178 explicit FindNamedClassConsumer(ASTContext *Context) 179 : Visitor(Context) {} 180 181 virtual void HandleTranslationUnit(clang::ASTContext &Context) { 182 Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 183 } 184 private: 185 FindNamedClassVisitor Visitor; 186 }; 187 188 class FindNamedClassAction : public clang::ASTFrontendAction { 189 public: 190 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( 191 clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 192 return std::unique_ptr<clang::ASTConsumer>( 193 new FindNamedClassConsumer(&Compiler.getASTContext())); 194 } 195 }; 196 197 int main(int argc, char **argv) { 198 if (argc > 1) { 199 clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]); 200 } 201 } 202 203 We store this into a file called FindClassDecls.cpp and create the 204 following CMakeLists.txt to link it: 205 206 :: 207 208 add_clang_executable(find-class-decls FindClassDecls.cpp) 209 210 target_link_libraries(find-class-decls clangTooling) 211 212 When running this tool over a small code snippet it will output all 213 declarations of a class n::m::C it found: 214 215 :: 216 217 $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }" 218 Found declaration at 1:29 219 220