| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 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: | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 28 | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 29 | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 30 | return std::unique_ptr<clang::ASTConsumer>( | 
|  | 31 | new FindNamedClassConsumer); | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 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 |  | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 115 | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 116 | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 117 | return std::unique_ptr<clang::ASTConsumer>( | 
|  | 118 | new FindNamedClassConsumer(&Compiler.getASTContext())); | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 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: | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 190 | virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 191 | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { | 
| Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 192 | return std::unique_ptr<clang::ASTConsumer>( | 
|  | 193 | new FindNamedClassConsumer(&Compiler.getASTContext())); | 
| Sean Silva | 93ca021 | 2012-12-13 01:10:46 +0000 | [diff] [blame] | 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 | set(LLVM_USED_LIBS clangTooling) | 
|  | 209 |  | 
|  | 210 | add_clang_executable(find-class-decls FindClassDecls.cpp) | 
|  | 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 |  |