Annotator.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. //
  2. // Copyright (c) 2008-2021 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include <clang/ASTMatchers/ASTMatchFinder.h>
  23. #include <clang/Tooling/CommonOptionsParser.h>
  24. #include <clang/Tooling/Refactoring.h>
  25. #include <unordered_map>
  26. #include <unordered_set>
  27. using namespace clang;
  28. using namespace clang::ast_matchers;
  29. using namespace clang::driver;
  30. using namespace clang::tooling;
  31. using namespace llvm;
  32. static cl::extrahelp commonHelp(CommonOptionsParser::HelpMessage);
  33. static cl::extrahelp moreHelp(
  34. "\tFor example, to run Annotator on all files in a subtree of the\n"
  35. "\tsource tree, use:\n"
  36. "\n"
  37. "\t find path/in/substree -name '*.cpp'|xargs Annotator -p build/path\n"
  38. "\n"
  39. "\tNote, that path/in/subtree and current directory should follow the\n"
  40. "\trules described above.\n"
  41. "\n"
  42. "Most probably you want to invoke 'annotate' built-in target instead of invoking this tool\n"
  43. "directly. The 'annotate' target invokes this tool in a right context prepared by build system.\n"
  44. "\n"
  45. );
  46. static cl::OptionCategory annotatorCategory("Annotator options");
  47. // ClangTool only takes a reference to the array list without owning it, so we need to keep the filtered list ourselves
  48. class PathFilter
  49. {
  50. public:
  51. template <typename Fn> PathFilter(const std::vector<std::string> sourcePaths, Fn fn)
  52. {
  53. std::copy_if(sourcePaths.begin(), sourcePaths.end(), std::back_inserter(pathList_), fn);
  54. }
  55. std::vector<std::string> GetPathList() {
  56. return pathList_;
  57. }
  58. private:
  59. std::vector<std::string> pathList_;
  60. };
  61. struct Data
  62. {
  63. std::unordered_set<std::string> exposedSymbols_;
  64. std::unordered_set<std::string> annotatedSymbols_;
  65. };
  66. static const std::string categories_[] = {"class", "enum"};
  67. static std::unordered_map<std::string, Data> categoryData_;
  68. class ExtractCallback : public MatchFinder::MatchCallback
  69. {
  70. public :
  71. virtual void run(const MatchFinder::MatchResult& result)
  72. {
  73. for (auto& i: categories_)
  74. {
  75. auto symbol = result.Nodes.getNodeAs<StringLiteral>(i);
  76. if (symbol)
  77. categoryData_[i].exposedSymbols_.insert(symbol->getString());
  78. }
  79. }
  80. virtual void onStartOfTranslationUnit()
  81. {
  82. static unsigned count = sizeof("Extracting") / sizeof(char) - 1;
  83. outs() << '.' << (++count % 100 ? "" : "\n"); // Sending a heart beat
  84. }
  85. };
  86. class AnnotateCallback : public MatchFinder::MatchCallback
  87. {
  88. public :
  89. AnnotateCallback(Replacements& replacements) :
  90. replacements_(replacements)
  91. {
  92. }
  93. virtual void run(const MatchFinder::MatchResult& result)
  94. {
  95. for (auto& i: categories_)
  96. {
  97. auto symbol = result.Nodes.getNodeAs<NamedDecl>(i);
  98. if (symbol)
  99. {
  100. auto& data = categoryData_[i];
  101. if (data.annotatedSymbols_.find(symbol->getName()) == data.annotatedSymbols_.end() &&
  102. data.exposedSymbols_.find(symbol->getName()) == data.exposedSymbols_.end())
  103. {
  104. replacements_.insert(Replacement(*result.SourceManager, symbol->getLocation(), 0, "NONSCRIPTABLE "));
  105. data.annotatedSymbols_.insert(symbol->getName());
  106. }
  107. }
  108. }
  109. }
  110. virtual void onStartOfTranslationUnit()
  111. {
  112. static unsigned count = sizeof("Annotating") / sizeof(char) - 1;
  113. outs() << '.' << (++count % 100 ? "" : "\n");
  114. }
  115. private:
  116. Replacements& replacements_;
  117. };
  118. int main(int argc, const char** argv)
  119. {
  120. // Parse the arguments and pass them to the the internal sub-tools
  121. CommonOptionsParser optionsParser(argc, argv, annotatorCategory);
  122. PathFilter bindingPathFilter
  123. (optionsParser.getSourcePathList(), [](const std::string& path) { return path.find("API.cpp") != std::string::npos; });
  124. PathFilter nonBindingPathFilter
  125. (optionsParser.getSourcePathList(), [](const std::string& path) { return path.find("API.cpp") == std::string::npos; });
  126. ClangTool bindingExtractor(optionsParser.getCompilations(), bindingPathFilter.GetPathList());
  127. RefactoringTool annotator(optionsParser.getCompilations(), nonBindingPathFilter.GetPathList());
  128. // Setup finder to match against AST nodes from existing AngelScript binding source files
  129. ExtractCallback extractCallback;
  130. MatchFinder bindingFinder;
  131. // Find exposed classes (they are registered through RegisterObjectType(), RegisterRefCounted(), RegisterObject(), etc)
  132. bindingFinder.addMatcher(
  133. memberCallExpr(
  134. callee(
  135. methodDecl(hasName("RegisterObjectType"))),
  136. hasArgument(0, stringLiteral().bind("class"))), &extractCallback);
  137. bindingFinder.addMatcher(
  138. callExpr(
  139. hasDeclaration(
  140. functionDecl(hasParameter(1, hasName("className")))),
  141. hasArgument(1, stringLiteral().bind("class"))), &extractCallback);
  142. // Find exposed enums
  143. bindingFinder.addMatcher(
  144. memberCallExpr(
  145. callee(
  146. methodDecl(hasName("RegisterEnum"))),
  147. hasArgument(0, stringLiteral().bind("enum"))), &extractCallback);
  148. // Setup finder to match against AST nodes for annotating Urho3D library source files
  149. AnnotateCallback annotateCallback(annotator.getReplacements());
  150. MatchFinder annotateFinder;
  151. // Find exported class declarations with Urho3D namespace
  152. annotateFinder.addMatcher(
  153. recordDecl(
  154. unless(hasAttr(attr::Annotate)),
  155. #ifndef _MSC_VER
  156. hasAttr(attr::Visibility),
  157. #else
  158. hasAttr(attr::DLLExport),
  159. #endif
  160. matchesName("^::Urho3D::")).bind("class"), &annotateCallback);
  161. // Find enum declarations with Urho3D namespace
  162. annotateFinder.addMatcher(
  163. enumDecl(
  164. unless(hasAttr(attr::Annotate)),
  165. matchesName("^::Urho3D::")).bind("enum"), &annotateCallback);
  166. // Unbuffered stdout stream to keep the Travis-CI's log flowing and thus prevent it from killing a potentially long running job
  167. outs().SetUnbuffered();
  168. // Success when both sub-tools are run successfully
  169. return (outs() << "Extracting", true) &&
  170. bindingExtractor.run(newFrontendActionFactory(&bindingFinder).get()) == EXIT_SUCCESS &&
  171. (outs() << "\nAnnotating", true) &&
  172. annotator.runAndSave(newFrontendActionFactory(&annotateFinder).get()) == EXIT_SUCCESS &&
  173. (outs() << "\n", true) ?
  174. EXIT_SUCCESS : EXIT_FAILURE;
  175. }