1 ==========================================================
2 How to write RecursiveASTVisitor based ASTFrontendActions.
3 ==========================================================
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
12 Creating a FrontendAction
13 =========================
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
26 class FindNamedClassAction : public clang::ASTFrontendAction {
28 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
29 clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
30 return std::make_unique<FindNamedClassConsumer>();
34 Creating an ASTConsumer
35 =======================
37 ASTConsumer is an interface used to write generic actions on an AST,
38 regardless of how the AST was produced. ASTConsumer provides many
39 different entry points, but for our use case the only one needed is
40 HandleTranslationUnit, which is called with the ASTContext for the
45 class FindNamedClassConsumer : public clang::ASTConsumer {
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());
53 // A RecursiveASTVisitor implementation.
54 FindNamedClassVisitor Visitor;
57 Using the RecursiveASTVisitor
58 =============================
60 Now that everything is hooked up, the next step is to implement a
61 RecursiveASTVisitor to extract the relevant information from the AST.
63 The RecursiveASTVisitor provides hooks of the form bool
64 VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
65 nodes, which are passed by-value. We only need to implement the methods
66 for the relevant node types.
68 Let's start by writing a RecursiveASTVisitor that visits all
73 class FindNamedClassVisitor
74 : public RecursiveASTVisitor<FindNamedClassVisitor> {
76 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
77 // For debugging, dumping the AST nodes will show which nodes are already
81 // The return value indicates whether we want the visitation to proceed.
82 // Return false to stop the traversal of the AST.
87 In the methods of our RecursiveASTVisitor we can now use the full power
88 of the Clang AST to drill through to the parts that are interesting for
89 us. For example, to find all class declaration with a certain name, we
90 can check for a specific qualified name:
94 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
95 if (Declaration->getQualifiedNameAsString() == "n::m::C")
100 Accessing the SourceManager and ASTContext
101 ==========================================
103 Some of the information about the AST, like source locations and global
104 identifier information, are not stored in the AST nodes themselves, but
105 in the ASTContext and its associated source manager. To retrieve them we
106 need to hand the ASTContext into our RecursiveASTVisitor implementation.
108 The ASTContext is available from the CompilerInstance during the call to
109 CreateASTConsumer. We can thus extract it there and hand it into our
110 freshly created FindNamedClassConsumer:
114 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
115 clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
116 return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());
119 Now that the ASTContext is available in the RecursiveASTVisitor, we can
120 do more interesting things with AST nodes, like looking up their source
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";
138 Putting it all together
139 =======================
141 Now we can combine all of the above into a small example program:
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"
151 using namespace clang;
153 class FindNamedClassVisitor
154 : public RecursiveASTVisitor<FindNamedClassVisitor> {
156 explicit FindNamedClassVisitor(ASTContext *Context)
157 : Context(Context) {}
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";
174 class FindNamedClassConsumer : public clang::ASTConsumer {
176 explicit FindNamedClassConsumer(ASTContext *Context)
177 : Visitor(Context) {}
179 virtual void HandleTranslationUnit(clang::ASTContext &Context) {
180 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
183 FindNamedClassVisitor Visitor;
186 class FindNamedClassAction : public clang::ASTFrontendAction {
188 virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
189 clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
190 return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());
194 int main(int argc, char **argv) {
196 clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]);
200 We store this into a file called FindClassDecls.cpp and create the
201 following CMakeLists.txt to link it:
205 set(LLVM_LINK_COMPONENTS
209 add_clang_executable(find-class-decls FindClassDecls.cpp)
211 target_link_libraries(find-class-decls
220 When running this tool over a small code snippet it will output all
221 declarations of a class n::m::C it found:
225 $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
226 Found declaration at 1:29