blob: dec1166bfac5a8796a1b5c448f600d0674adc112 [file] [log] [blame]
Manuel Klimek74eb4af2012-04-26 08:49:54 +00001<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2 "http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<title>How to write RecursiveASTVisitor based ASTFrontendActions.</title>
6<link type="text/css" rel="stylesheet" href="../menu.css">
7<link type="text/css" rel="stylesheet" href="../content.css">
8</head>
9<body>
10<div id="content">
11
12<h1>How to write RecursiveASTVisitor based ASTFrontendActions.</h1>
13
14<!-- ======================================================================= -->
15<h2 id="intro">Introduction</h2>
16<!-- ======================================================================= -->
17
18In this tutorial you will learn how to create a FrontendAction that uses
19a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified name.
20
21<!-- ======================================================================= -->
22<h2 id="action">Creating a FrontendAction</h2>
23<!-- ======================================================================= -->
24
25<p>When writing a clang based tool like a Clang Plugin or a standalone tool
26based on LibTooling, the common entry point is the FrontendAction.
27FrontendAction is an interface that allows execution of user specific actions
28as part of the compilation. To run tools over the AST clang provides the
29convenience interface ASTFrontendAction, which takes care of executing the
30action. The only part left is to implement the CreateASTConsumer method that
31returns an ASTConsumer per translation unit.</p>
32<pre>
33 class FindNamedClassAction : public clang::ASTFrontendAction {
34 public:
35 virtual clang::ASTConsumer *CreateASTConsumer(
36 clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
37 return new FindNamedClassConsumer;
38 }
39 };
40</pre>
41
42<!-- ======================================================================= -->
43<h2 id="consumer">Creating an ASTConsumer</h2>
44<!-- ======================================================================= -->
45
46<p>ASTConsumer is an interface used to write generic actions on an AST,
47regardless of how the AST was produced. ASTConsumer provides many different
48entry points, but for our use case the only one needed is HandleTranslationUnit,
49which is called with the ASTContext for the translation unit.</p>
50<pre>
51 class FindNamedClassConsumer : public clang::ASTConsumer {
52 public:
53 virtual void HandleTranslationUnit(clang::ASTContext &amp;Context) {
54 // Traversing the translation unit decl via a RecursiveASTVisitor
55 // will visit all nodes in the AST.
56 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
57 }
58 private:
59 // A RecursiveASTVisitor implementation.
60 FindNamedClassVisitor Visitor;
61 };
62</pre>
63
64<!-- ======================================================================= -->
65<h2 id="rav">Using the RecursiveASTVisitor</h2>
66<!-- ======================================================================= -->
67
68<p>Now that everything is hooked up, the next step is to implement a
69RecursiveASTVisitor to extract the relevant information from the AST.</p>
70<p>The RecursiveASTVisitor provides hooks of the form
71bool VisitNodeType(NodeType *) for most AST nodes; the exception are TypeLoc
72nodes, which are passed by-value. We only need to implement the methods for the
73relevant node types.
74</p>
75<p>Let's start by writing a RecursiveASTVisitor that visits all CXXRecordDecl's.
76<pre>
77 class FindNamedClassVisitor
78 : public RecursiveASTVisitor&lt;FindNamedClassVisitor> {
79 public:
80 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
81 // For debugging, dumping the AST nodes will show which nodes are already
82 // being visited.
83 Declaration->dump();
84
85 // The return value indicates whether we want the visitation to proceed.
86 // Return false to stop the traversal of the AST.
87 return true;
88 }
89 };
90</pre>
91</p>
92<p>In the methods of our RecursiveASTVisitor we can now use the full power of
93the Clang AST to drill through to the parts that are interesting for us. For
94example, to find all class declaration with a certain name, we can check for a
95specific qualified name:
96<pre>
97 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
98 if (Declaration->getQualifiedNameAsString() == "n::m::C")
99 Declaration->dump();
100 return true;
101 }
102</pre>
103</p>
104
105<!-- ======================================================================= -->
106<h2 id="context">Accessing the SourceManager and ASTContext</h2>
107<!-- ======================================================================= -->
108
109<p>Some of the information about the AST, like source locations and global
110identifier information, are not stored in the AST nodes themselves, but in
111the ASTContext and its associated source manager. To retrieve them we need to
112hand the ASTContext into our RecursiveASTVisitor implementation.</p>
113<p>The ASTContext is available from the CompilerInstance during the call
114to CreateASTConsumer. We can thus extract it there and hand it into our
115freshly created FindNamedClassConsumer:</p>
116<pre>
117 virtual clang::ASTConsumer *CreateASTConsumer(
118 clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
119 return new FindNamedClassConsumer(<b>&amp;Compiler.getASTContext()</b>);
120 }
121</pre>
122
123<p>Now that the ASTContext is available in the RecursiveASTVisitor, we can do
124more interesting things with AST nodes, like looking up their source
125locations:</p>
126<pre>
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() &lt;&lt; "Found declaration at "
134 &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
135 &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\n";
136 }
137 return true;
138 }
139</pre>
140
141<!-- ======================================================================= -->
142<h2 id="full">Putting it all together</h2>
143<!-- ======================================================================= -->
144
145<p>Now we can combine all of the above into a small example program:</p>
146<pre>
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&lt;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() &lt;&lt; "Found declaration at "
166 &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
167 &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\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 &amp;Context) {
182 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
183 }
184 private:
185 FindNamedClassVisitor Visitor;
186 };
187
188 class FindNamedClassAction : public clang::ASTFrontendAction {
189 public:
190 virtual clang::ASTConsumer *CreateASTConsumer(
191 clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
192 return new FindNamedClassConsumer(&amp;Compiler.getASTContext());
193 }
194 };
195
196 int main(int argc, char **argv) {
197 if (argc > 1) {
198 clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
199 }
200 }
201</pre>
202
203<p>We store this into a file called FindClassDecls.cpp and create the following
204CMakeLists.txt to link it:</p>
205<pre>
206set(LLVM_USED_LIBS clangTooling)
207
208add_clang_executable(find-class-decls FindClassDecls.cpp)
209</pre>
210
211<p>When running this tool over a small code snippet it will output all
212declarations of a class n::m::C it found:</p>
213<pre>
214 $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
215 Found declaration at 1:29
216</pre>
217
218</div>
219</body>
220</html>
221