Annotator.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include <clang/ASTMatchers/ASTMatchFinder.h>
  4. #include <clang/Tooling/CommonOptionsParser.h>
  5. #include <clang/Tooling/Refactoring.h>
  6. #include <unordered_map>
  7. #include <unordered_set>
  8. using namespace clang;
  9. using namespace clang::ast_matchers;
  10. using namespace clang::driver;
  11. using namespace clang::tooling;
  12. using namespace llvm;
  13. static cl::extrahelp commonHelp(CommonOptionsParser::HelpMessage);
  14. static cl::extrahelp moreHelp(
  15. "\tFor example, to run Annotator on all files in a subtree of the\n"
  16. "\tsource tree, use:\n"
  17. "\n"
  18. "\t find path/in/substree -name '*.cpp'|xargs Annotator -p build/path\n"
  19. "\n"
  20. "\tNote, that path/in/subtree and current directory should follow the\n"
  21. "\trules described above.\n"
  22. "\n"
  23. "Most probably you want to invoke 'annotate' built-in target instead of invoking this tool\n"
  24. "directly. The 'annotate' target invokes this tool in a right context prepared by build system.\n"
  25. "\n"
  26. );
  27. static cl::OptionCategory annotatorCategory("Annotator options");
  28. // ClangTool only takes a reference to the array list without owning it, so we need to keep the filtered list ourselves
  29. class PathFilter
  30. {
  31. public:
  32. template <typename Fn> PathFilter(const std::vector<std::string> sourcePaths, Fn fn)
  33. {
  34. std::copy_if(sourcePaths.begin(), sourcePaths.end(), std::back_inserter(pathList_), fn);
  35. }
  36. std::vector<std::string> GetPathList() {
  37. return pathList_;
  38. }
  39. private:
  40. std::vector<std::string> pathList_;
  41. };
  42. struct Data
  43. {
  44. std::unordered_set<std::string> exposedSymbols_;
  45. std::unordered_set<std::string> annotatedSymbols_;
  46. };
  47. static const std::string categories_[] = {"class", "enum"};
  48. static std::unordered_map<std::string, Data> categoryData_;
  49. class ExtractCallback : public MatchFinder::MatchCallback
  50. {
  51. public :
  52. virtual void run(const MatchFinder::MatchResult& result)
  53. {
  54. for (auto& i: categories_)
  55. {
  56. auto symbol = result.Nodes.getNodeAs<StringLiteral>(i);
  57. if (symbol)
  58. categoryData_[i].exposedSymbols_.insert(symbol->getString());
  59. }
  60. }
  61. virtual void onStartOfTranslationUnit()
  62. {
  63. static unsigned count = sizeof("Extracting") / sizeof(char) - 1;
  64. outs() << '.' << (++count % 100 ? "" : "\n"); // Sending a heart beat
  65. }
  66. };
  67. class AnnotateCallback : public MatchFinder::MatchCallback
  68. {
  69. public :
  70. AnnotateCallback(Replacements& replacements) :
  71. replacements_(replacements)
  72. {
  73. }
  74. virtual void run(const MatchFinder::MatchResult& result)
  75. {
  76. for (auto& i: categories_)
  77. {
  78. auto symbol = result.Nodes.getNodeAs<NamedDecl>(i);
  79. if (symbol)
  80. {
  81. auto& data = categoryData_[i];
  82. if (data.annotatedSymbols_.find(symbol->getName()) == data.annotatedSymbols_.end() &&
  83. data.exposedSymbols_.find(symbol->getName()) == data.exposedSymbols_.end())
  84. {
  85. replacements_.insert(Replacement(*result.SourceManager, symbol->getLocation(), 0, "NONSCRIPTABLE "));
  86. data.annotatedSymbols_.insert(symbol->getName());
  87. }
  88. }
  89. }
  90. }
  91. virtual void onStartOfTranslationUnit()
  92. {
  93. static unsigned count = sizeof("Annotating") / sizeof(char) - 1;
  94. outs() << '.' << (++count % 100 ? "" : "\n");
  95. }
  96. private:
  97. Replacements& replacements_;
  98. };
  99. int main(int argc, const char** argv)
  100. {
  101. // Parse the arguments and pass them to the the internal sub-tools
  102. CommonOptionsParser optionsParser(argc, argv, annotatorCategory);
  103. PathFilter bindingPathFilter
  104. (optionsParser.getSourcePathList(), [](const std::string& path) { return path.find("API.cpp") != std::string::npos; });
  105. PathFilter nonBindingPathFilter
  106. (optionsParser.getSourcePathList(), [](const std::string& path) { return path.find("API.cpp") == std::string::npos; });
  107. ClangTool bindingExtractor(optionsParser.getCompilations(), bindingPathFilter.GetPathList());
  108. RefactoringTool annotator(optionsParser.getCompilations(), nonBindingPathFilter.GetPathList());
  109. // Setup finder to match against AST nodes from existing AngelScript binding source files
  110. ExtractCallback extractCallback;
  111. MatchFinder bindingFinder;
  112. // Find exposed classes (they are registered through RegisterObjectType(), RegisterRefCounted(), RegisterObject(), etc)
  113. bindingFinder.addMatcher(
  114. memberCallExpr(
  115. callee(
  116. methodDecl(hasName("RegisterObjectType"))),
  117. hasArgument(0, stringLiteral().bind("class"))), &extractCallback);
  118. bindingFinder.addMatcher(
  119. callExpr(
  120. hasDeclaration(
  121. functionDecl(hasParameter(1, hasName("className")))),
  122. hasArgument(1, stringLiteral().bind("class"))), &extractCallback);
  123. // Find exposed enums
  124. bindingFinder.addMatcher(
  125. memberCallExpr(
  126. callee(
  127. methodDecl(hasName("RegisterEnum"))),
  128. hasArgument(0, stringLiteral().bind("enum"))), &extractCallback);
  129. // Setup finder to match against AST nodes for annotating Urho3D library source files
  130. AnnotateCallback annotateCallback(annotator.getReplacements());
  131. MatchFinder annotateFinder;
  132. // Find exported class declarations with Urho3D namespace
  133. annotateFinder.addMatcher(
  134. recordDecl(
  135. unless(hasAttr(attr::Annotate)),
  136. #ifndef _MSC_VER
  137. hasAttr(attr::Visibility),
  138. #else
  139. hasAttr(attr::DLLExport),
  140. #endif
  141. matchesName("^::Urho3D::")).bind("class"), &annotateCallback);
  142. // Find enum declarations with Urho3D namespace
  143. annotateFinder.addMatcher(
  144. enumDecl(
  145. unless(hasAttr(attr::Annotate)),
  146. matchesName("^::Urho3D::")).bind("enum"), &annotateCallback);
  147. // Unbuffered stdout stream to keep the Travis-CI's log flowing and thus prevent it from killing a potentially long running job
  148. outs().SetUnbuffered();
  149. // Success when both sub-tools are run successfully
  150. return (outs() << "Extracting", true) &&
  151. bindingExtractor.run(newFrontendActionFactory(&bindingFinder).get()) == EXIT_SUCCESS &&
  152. (outs() << "\nAnnotating", true) &&
  153. annotator.runAndSave(newFrontendActionFactory(&annotateFinder).get()) == EXIT_SUCCESS &&
  154. (outs() << "\n", true) ?
  155. EXIT_SUCCESS : EXIT_FAILURE;
  156. }