forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			225 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			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>
 | 
						|
 | 
						|
<!--#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>
 | 
						|
 |