diff options
Diffstat (limited to 'docs/RAVFrontendAction.html')
-rw-r--r-- | docs/RAVFrontendAction.html | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/docs/RAVFrontendAction.html b/docs/RAVFrontendAction.html new file mode 100644 index 0000000..b30cd57 --- /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 &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 &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<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 &Compiler, llvm::StringRef InFile) { + return new FindNamedClassConsumer(<b>&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() << "Found declaration at " + << FullLocation.getSpellingLineNumber() << ":" + << FullLocation.getSpellingColumnNumber() << "\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<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() << "Found declaration at " + << FullLocation.getSpellingLineNumber() << ":" + << FullLocation.getSpellingColumnNumber() << "\n"; + } + return true; + } + + private: + ASTContext *Context; + }; + + class FindNamedClassConsumer : public clang::ASTConsumer { + public: + explicit FindNamedClassConsumer(ASTContext *Context) + : Visitor(Context) {} + + virtual void HandleTranslationUnit(clang::ASTContext &Context) { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + private: + FindNamedClassVisitor Visitor; + }; + + class FindNamedClassAction : public clang::ASTFrontendAction { + public: + virtual clang::ASTConsumer *CreateASTConsumer( + clang::CompilerInstance &Compiler, llvm::StringRef InFile) { + return new FindNamedClassConsumer(&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> + |