aboutsummaryrefslogtreecommitdiff
path: root/docs/RAVFrontendAction.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/RAVFrontendAction.html')
-rw-r--r--docs/RAVFrontendAction.html224
1 files changed, 224 insertions, 0 deletions
diff --git a/docs/RAVFrontendAction.html b/docs/RAVFrontendAction.html
new file mode 100644
index 000000000000..b30cd573d932
--- /dev/null
+++ b/docs/RAVFrontendAction.html
@@ -0,0 +1,224 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>How to write RecursiveASTVisitor based ASTFrontendActions.</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>How to write RecursiveASTVisitor based ASTFrontendActions.</h1>
+
+<!-- ======================================================================= -->
+<h2 id="intro">Introduction</h2>
+<!-- ======================================================================= -->
+
+In this tutorial you will learn how to create a FrontendAction that uses
+a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified name.
+
+<!-- ======================================================================= -->
+<h2 id="action">Creating a FrontendAction</h2>
+<!-- ======================================================================= -->
+
+<p>When writing a clang based tool like a Clang Plugin or a standalone tool
+based on LibTooling, the common entry point is the FrontendAction.
+FrontendAction is an interface that allows execution of user specific actions
+as part of the compilation. To run tools over the AST clang provides the
+convenience interface ASTFrontendAction, which takes care of executing the
+action. The only part left is to implement the CreateASTConsumer method that
+returns an ASTConsumer per translation unit.</p>
+<pre>
+ class FindNamedClassAction : public clang::ASTFrontendAction {
+ public:
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer;
+ }
+ };
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="consumer">Creating an ASTConsumer</h2>
+<!-- ======================================================================= -->
+
+<p>ASTConsumer is an interface used to write generic actions on an AST,
+regardless of how the AST was produced. ASTConsumer provides many different
+entry points, but for our use case the only one needed is HandleTranslationUnit,
+which is called with the ASTContext for the translation unit.</p>
+<pre>
+ class FindNamedClassConsumer : public clang::ASTConsumer {
+ public:
+ virtual void HandleTranslationUnit(clang::ASTContext &amp;Context) {
+ // Traversing the translation unit decl via a RecursiveASTVisitor
+ // will visit all nodes in the AST.
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ }
+ private:
+ // A RecursiveASTVisitor implementation.
+ FindNamedClassVisitor Visitor;
+ };
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="rav">Using the RecursiveASTVisitor</h2>
+<!-- ======================================================================= -->
+
+<p>Now that everything is hooked up, the next step is to implement a
+RecursiveASTVisitor to extract the relevant information from the AST.</p>
+<p>The RecursiveASTVisitor provides hooks of the form
+bool VisitNodeType(NodeType *) for most AST nodes; the exception are TypeLoc
+nodes, which are passed by-value. We only need to implement the methods for the
+relevant node types.
+</p>
+<p>Let's start by writing a RecursiveASTVisitor that visits all CXXRecordDecl's.
+<pre>
+ class FindNamedClassVisitor
+ : public RecursiveASTVisitor&lt;FindNamedClassVisitor> {
+ public:
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ // For debugging, dumping the AST nodes will show which nodes are already
+ // being visited.
+ Declaration->dump();
+
+ // The return value indicates whether we want the visitation to proceed.
+ // Return false to stop the traversal of the AST.
+ return true;
+ }
+ };
+</pre>
+</p>
+<p>In the methods of our RecursiveASTVisitor we can now use the full power of
+the Clang AST to drill through to the parts that are interesting for us. For
+example, to find all class declaration with a certain name, we can check for a
+specific qualified name:
+<pre>
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C")
+ Declaration->dump();
+ return true;
+ }
+</pre>
+</p>
+
+<!-- ======================================================================= -->
+<h2 id="context">Accessing the SourceManager and ASTContext</h2>
+<!-- ======================================================================= -->
+
+<p>Some of the information about the AST, like source locations and global
+identifier information, are not stored in the AST nodes themselves, but in
+the ASTContext and its associated source manager. To retrieve them we need to
+hand the ASTContext into our RecursiveASTVisitor implementation.</p>
+<p>The ASTContext is available from the CompilerInstance during the call
+to CreateASTConsumer. We can thus extract it there and hand it into our
+freshly created FindNamedClassConsumer:</p>
+<pre>
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer(<b>&amp;Compiler.getASTContext()</b>);
+ }
+</pre>
+
+<p>Now that the ASTContext is available in the RecursiveASTVisitor, we can do
+more interesting things with AST nodes, like looking up their source
+locations:</p>
+<pre>
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C") {
+ // getFullLoc uses the ASTContext's SourceManager to resolve the source
+ // location and break it up into its line and column parts.
+ FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
+ if (FullLocation.isValid())
+ llvm::outs() &lt;&lt; "Found declaration at "
+ &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
+ &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\n";
+ }
+ return true;
+ }
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="full">Putting it all together</h2>
+<!-- ======================================================================= -->
+
+<p>Now we can combine all of the above into a small example program:</p>
+<pre>
+ #include "clang/AST/ASTConsumer.h"
+ #include "clang/AST/RecursiveASTVisitor.h"
+ #include "clang/Frontend/CompilerInstance.h"
+ #include "clang/Frontend/FrontendAction.h"
+ #include "clang/Tooling/Tooling.h"
+
+ using namespace clang;
+
+ class FindNamedClassVisitor
+ : public RecursiveASTVisitor&lt;FindNamedClassVisitor> {
+ public:
+ explicit FindNamedClassVisitor(ASTContext *Context)
+ : Context(Context) {}
+
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C") {
+ FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
+ if (FullLocation.isValid())
+ llvm::outs() &lt;&lt; "Found declaration at "
+ &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
+ &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\n";
+ }
+ return true;
+ }
+
+ private:
+ ASTContext *Context;
+ };
+
+ class FindNamedClassConsumer : public clang::ASTConsumer {
+ public:
+ explicit FindNamedClassConsumer(ASTContext *Context)
+ : Visitor(Context) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &amp;Context) {
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ }
+ private:
+ FindNamedClassVisitor Visitor;
+ };
+
+ class FindNamedClassAction : public clang::ASTFrontendAction {
+ public:
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer(&amp;Compiler.getASTContext());
+ }
+ };
+
+ int main(int argc, char **argv) {
+ if (argc > 1) {
+ clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
+ }
+ }
+</pre>
+
+<p>We store this into a file called FindClassDecls.cpp and create the following
+CMakeLists.txt to link it:</p>
+<pre>
+set(LLVM_USED_LIBS clangTooling)
+
+add_clang_executable(find-class-decls FindClassDecls.cpp)
+</pre>
+
+<p>When running this tool over a small code snippet it will output all
+declarations of a class n::m::C it found:</p>
+<pre>
+ $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
+ Found declaration at 1:29
+</pre>
+
+</div>
+</body>
+</html>
+