main.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "clang/ASTMatchers/ASTMatchFinder.h"
  5. #include "clang/ASTMatchers/ASTMatchers.h"
  6. #include "clang/Tooling/CommonOptionsParser.h"
  7. #include "clang/Tooling/Refactoring.h"
  8. namespace cam = ::clang::ast_matchers;
  9. namespace ct = ::clang::tooling;
  10. namespace Carbon {
  11. class Matcher : public cam::MatchFinder::MatchCallback {
  12. public:
  13. explicit Matcher(std::map<std::string, ct::Replacements>& in_replacements)
  14. : replacements(&in_replacements) {}
  15. ~Matcher() override = default;
  16. void AddReplacement(const clang::SourceManager& sm,
  17. clang::CharSourceRange range,
  18. llvm::StringRef replacement_text) {
  19. if (!range.isValid()) {
  20. llvm::errs() << "Invalid range: " << range.getAsRange().printToString(sm)
  21. << "\n";
  22. return;
  23. }
  24. if (sm.getDecomposedLoc(range.getBegin()).first !=
  25. sm.getDecomposedLoc(range.getEnd()).first) {
  26. llvm::errs() << "Range spans macro expansions: "
  27. << range.getAsRange().printToString(sm) << "\n";
  28. return;
  29. }
  30. if (sm.getFileID(range.getBegin()) != sm.getFileID(range.getEnd())) {
  31. llvm::errs() << "Range spans files: "
  32. << range.getAsRange().printToString(sm) << "\n";
  33. return;
  34. }
  35. auto rep =
  36. ct::Replacement(sm, sm.getExpansionRange(range), replacement_text);
  37. auto err = (*replacements)[std::string(rep.getFilePath())].add(rep);
  38. if (err) {
  39. llvm::report_fatal_error("Error with replacement `" + rep.toString() +
  40. "`: " + llvm::toString(std::move(err)) + "\n");
  41. }
  42. }
  43. private:
  44. std::map<std::string, ct::Replacements>* replacements;
  45. };
  46. class FnInserter : public Matcher {
  47. public:
  48. explicit FnInserter(std::map<std::string, ct::Replacements>& in_replacements,
  49. cam::MatchFinder* finder)
  50. : Matcher(in_replacements) {
  51. finder->addMatcher(cam::functionDecl(cam::isExpansionInMainFile(),
  52. cam::hasTrailingReturn())
  53. .bind(Label),
  54. this);
  55. }
  56. void run(const cam::MatchFinder::MatchResult& result) override {
  57. // The matched 'if' statement was bound to 'ifStmt'.
  58. const auto* decl = result.Nodes.getNodeAs<clang::FunctionDecl>(Label);
  59. if (!decl) {
  60. llvm::report_fatal_error(std::string("getNodeAs failed for ") + Label);
  61. }
  62. auto begin = decl->getBeginLoc();
  63. // Replace the first token in the range, `auto`.
  64. auto range = clang::CharSourceRange::getTokenRange(begin, begin);
  65. AddReplacement(*(result.SourceManager), range, "fn");
  66. }
  67. private:
  68. static constexpr char Label[] = "FnInserter";
  69. };
  70. } // namespace Carbon
  71. auto main(int argc, const char** argv) -> int {
  72. llvm::cl::OptionCategory category("C++ refactoring options");
  73. clang::tooling::CommonOptionsParser op(argc, argv, category);
  74. clang::tooling::RefactoringTool tool(op.getCompilations(),
  75. op.getSourcePathList());
  76. // Set up AST matcher callbacks.
  77. cam::MatchFinder finder;
  78. Carbon::FnInserter fn_inserter(tool.getReplacements(), &finder);
  79. return tool.runAndSave(
  80. clang::tooling::newFrontendActionFactory(&finder).get());
  81. }