RAVFrontendAction.rst 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. ==========================================================
  2. How to write RecursiveASTVisitor based ASTFrontendActions.
  3. ==========================================================
  4. Introduction
  5. ============
  6. NOTE: this document applies to the original Clang project, not the DirectX
  7. Compiler. It's made available for informational purposes only.
  8. In this tutorial you will learn how to create a FrontendAction that uses
  9. a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified
  10. name.
  11. Creating a FrontendAction
  12. =========================
  13. When writing a clang based tool like a Clang Plugin or a standalone tool
  14. based on LibTooling, the common entry point is the FrontendAction.
  15. FrontendAction is an interface that allows execution of user specific
  16. actions as part of the compilation. To run tools over the AST clang
  17. provides the convenience interface ASTFrontendAction, which takes care
  18. of executing the action. The only part left is to implement the
  19. CreateASTConsumer method that returns an ASTConsumer per translation
  20. unit.
  21. ::
  22. class FindNamedClassAction : public clang::ASTFrontendAction {
  23. public:
  24. virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
  25. clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
  26. return std::unique_ptr<clang::ASTConsumer>(
  27. new FindNamedClassConsumer);
  28. }
  29. };
  30. Creating an ASTConsumer
  31. =======================
  32. ASTConsumer is an interface used to write generic actions on an AST,
  33. regardless of how the AST was produced. ASTConsumer provides many
  34. different entry points, but for our use case the only one needed is
  35. HandleTranslationUnit, which is called with the ASTContext for the
  36. translation unit.
  37. ::
  38. class FindNamedClassConsumer : public clang::ASTConsumer {
  39. public:
  40. virtual void HandleTranslationUnit(clang::ASTContext &Context) {
  41. // Traversing the translation unit decl via a RecursiveASTVisitor
  42. // will visit all nodes in the AST.
  43. Visitor.TraverseDecl(Context.getTranslationUnitDecl());
  44. }
  45. private:
  46. // A RecursiveASTVisitor implementation.
  47. FindNamedClassVisitor Visitor;
  48. };
  49. Using the RecursiveASTVisitor
  50. =============================
  51. Now that everything is hooked up, the next step is to implement a
  52. RecursiveASTVisitor to extract the relevant information from the AST.
  53. The RecursiveASTVisitor provides hooks of the form bool
  54. VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
  55. nodes, which are passed by-value. We only need to implement the methods
  56. for the relevant node types.
  57. Let's start by writing a RecursiveASTVisitor that visits all
  58. CXXRecordDecl's.
  59. ::
  60. class FindNamedClassVisitor
  61. : public RecursiveASTVisitor<FindNamedClassVisitor> {
  62. public:
  63. bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
  64. // For debugging, dumping the AST nodes will show which nodes are already
  65. // being visited.
  66. Declaration->dump();
  67. // The return value indicates whether we want the visitation to proceed.
  68. // Return false to stop the traversal of the AST.
  69. return true;
  70. }
  71. };
  72. In the methods of our RecursiveASTVisitor we can now use the full power
  73. of the Clang AST to drill through to the parts that are interesting for
  74. us. For example, to find all class declaration with a certain name, we
  75. can check for a specific qualified name:
  76. ::
  77. bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
  78. if (Declaration->getQualifiedNameAsString() == "n::m::C")
  79. Declaration->dump();
  80. return true;
  81. }
  82. Accessing the SourceManager and ASTContext
  83. ==========================================
  84. Some of the information about the AST, like source locations and global
  85. identifier information, are not stored in the AST nodes themselves, but
  86. in the ASTContext and its associated source manager. To retrieve them we
  87. need to hand the ASTContext into our RecursiveASTVisitor implementation.
  88. The ASTContext is available from the CompilerInstance during the call to
  89. CreateASTConsumer. We can thus extract it there and hand it into our
  90. freshly created FindNamedClassConsumer:
  91. ::
  92. virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
  93. clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
  94. return std::unique_ptr<clang::ASTConsumer>(
  95. new FindNamedClassConsumer(&Compiler.getASTContext()));
  96. }
  97. Now that the ASTContext is available in the RecursiveASTVisitor, we can
  98. do more interesting things with AST nodes, like looking up their source
  99. locations:
  100. ::
  101. bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
  102. if (Declaration->getQualifiedNameAsString() == "n::m::C") {
  103. // getFullLoc uses the ASTContext's SourceManager to resolve the source
  104. // location and break it up into its line and column parts.
  105. FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
  106. if (FullLocation.isValid())
  107. llvm::outs() << "Found declaration at "
  108. << FullLocation.getSpellingLineNumber() << ":"
  109. << FullLocation.getSpellingColumnNumber() << "\n";
  110. }
  111. return true;
  112. }
  113. Putting it all together
  114. =======================
  115. Now we can combine all of the above into a small example program:
  116. ::
  117. #include "clang/AST/ASTConsumer.h"
  118. #include "clang/AST/RecursiveASTVisitor.h"
  119. #include "clang/Frontend/CompilerInstance.h"
  120. #include "clang/Frontend/FrontendAction.h"
  121. #include "clang/Tooling/Tooling.h"
  122. using namespace clang;
  123. class FindNamedClassVisitor
  124. : public RecursiveASTVisitor<FindNamedClassVisitor> {
  125. public:
  126. explicit FindNamedClassVisitor(ASTContext *Context)
  127. : Context(Context) {}
  128. bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
  129. if (Declaration->getQualifiedNameAsString() == "n::m::C") {
  130. FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
  131. if (FullLocation.isValid())
  132. llvm::outs() << "Found declaration at "
  133. << FullLocation.getSpellingLineNumber() << ":"
  134. << FullLocation.getSpellingColumnNumber() << "\n";
  135. }
  136. return true;
  137. }
  138. private:
  139. ASTContext *Context;
  140. };
  141. class FindNamedClassConsumer : public clang::ASTConsumer {
  142. public:
  143. explicit FindNamedClassConsumer(ASTContext *Context)
  144. : Visitor(Context) {}
  145. virtual void HandleTranslationUnit(clang::ASTContext &Context) {
  146. Visitor.TraverseDecl(Context.getTranslationUnitDecl());
  147. }
  148. private:
  149. FindNamedClassVisitor Visitor;
  150. };
  151. class FindNamedClassAction : public clang::ASTFrontendAction {
  152. public:
  153. virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
  154. clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
  155. return std::unique_ptr<clang::ASTConsumer>(
  156. new FindNamedClassConsumer(&Compiler.getASTContext()));
  157. }
  158. };
  159. int main(int argc, char **argv) {
  160. if (argc > 1) {
  161. clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
  162. }
  163. }
  164. We store this into a file called FindClassDecls.cpp and create the
  165. following CMakeLists.txt to link it:
  166. ::
  167. set(LLVM_USED_LIBS clangTooling)
  168. add_clang_executable(find-class-decls FindClassDecls.cpp)
  169. When running this tool over a small code snippet it will output all
  170. declarations of a class n::m::C it found:
  171. ::
  172. $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
  173. Found declaration at 1:29