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::unique_ptr<clang::ASTConsumer>(
31              new FindNamedClassConsumer);
32        }
33      };
34
35Creating an ASTConsumer
36=======================
37
38ASTConsumer is an interface used to write generic actions on an AST,
39regardless of how the AST was produced. ASTConsumer provides many
40different entry points, but for our use case the only one needed is
41HandleTranslationUnit, which is called with the ASTContext for the
42translation 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
58Using the RecursiveASTVisitor
59=============================
60
61Now that everything is hooked up, the next step is to implement a
62RecursiveASTVisitor to extract the relevant information from the AST.
63
64The RecursiveASTVisitor provides hooks of the form bool
65VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
66nodes, which are passed by-value. We only need to implement the methods
67for the relevant node types.
68
69Let's start by writing a RecursiveASTVisitor that visits all
70CXXRecordDecl'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
88In the methods of our RecursiveASTVisitor we can now use the full power
89of the Clang AST to drill through to the parts that are interesting for
90us. For example, to find all class declaration with a certain name, we
91can 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
101Accessing the SourceManager and ASTContext
102==========================================
103
104Some of the information about the AST, like source locations and global
105identifier information, are not stored in the AST nodes themselves, but
106in the ASTContext and its associated source manager. To retrieve them we
107need to hand the ASTContext into our RecursiveASTVisitor implementation.
108
109The ASTContext is available from the CompilerInstance during the call to
110CreateASTConsumer. We can thus extract it there and hand it into our
111freshly 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
121Now that the ASTContext is available in the RecursiveASTVisitor, we can
122do more interesting things with AST nodes, like looking up their source
123locations:
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->getBeginLoc());
132          if (FullLocation.isValid())
133            llvm::outs() << "Found declaration at "
134                         << FullLocation.getSpellingLineNumber() << ":"
135                         << FullLocation.getSpellingColumnNumber() << "\n";
136        }
137        return true;
138      }
139
140Putting it all together
141=======================
142
143Now 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->getBeginLoc());
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(std::make_unique<FindNamedClassAction>(), argv[1]);
200        }
201      }
202
203We store this into a file called FindClassDecls.cpp and create the
204following 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
212When running this tool over a small code snippet it will output all
213declarations 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