зеркало из https://github.com/microsoft/clang-1.git
Adds a small tutorial on how to write RAV based ASTFrontendActions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@155627 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
fd93630cce
Коммит
d9ed0fd0d1
|
@ -0,0 +1,221 @@
|
|||
<!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>
|
||||
<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>
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include "clang/AST/AST.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
@ -67,5 +68,5 @@ protected:
|
|||
|
||||
}
|
||||
|
||||
static FrontendPluginRegistry::Add<PrintFunctionNamesAction>
|
||||
static FrontendPluginRegistry::Add<SyntaxOnlyAction>
|
||||
X("print-fns", "print function names");
|
||||
|
|
Загрузка…
Ссылка в новой задаче