зеркало из https://github.com/microsoft/clang-1.git
222 строки
8.3 KiB
HTML
222 строки
8.3 KiB
HTML
<!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>
|
|
|