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