RefactoringActionRulesTest.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. //===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "ReplacementTest.h"
  9. #include "RewriterTestContext.h"
  10. #include "clang/Tooling/Refactoring.h"
  11. #include "clang/Tooling/Refactoring/Extract/Extract.h"
  12. #include "clang/Tooling/Refactoring/RefactoringAction.h"
  13. #include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
  14. #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
  15. #include "clang/Tooling/Tooling.h"
  16. #include "llvm/Support/Errc.h"
  17. #include "gtest/gtest.h"
  18. using namespace clang;
  19. using namespace tooling;
  20. namespace {
  21. class RefactoringActionRulesTest : public ::testing::Test {
  22. protected:
  23. void SetUp() override {
  24. Context.Sources.setMainFileID(
  25. Context.createInMemoryFile("input.cpp", DefaultCode));
  26. }
  27. RewriterTestContext Context;
  28. std::string DefaultCode = std::string(100, 'a');
  29. };
  30. Expected<AtomicChanges>
  31. createReplacements(const std::unique_ptr<RefactoringActionRule> &Rule,
  32. RefactoringRuleContext &Context) {
  33. class Consumer final : public RefactoringResultConsumer {
  34. void handleError(llvm::Error Err) override { Result = std::move(Err); }
  35. void handle(AtomicChanges SourceReplacements) override {
  36. Result = std::move(SourceReplacements);
  37. }
  38. void handle(SymbolOccurrences Occurrences) override {
  39. RefactoringResultConsumer::handle(std::move(Occurrences));
  40. }
  41. public:
  42. Optional<Expected<AtomicChanges>> Result;
  43. };
  44. Consumer C;
  45. Rule->invoke(C, Context);
  46. return std::move(*C.Result);
  47. }
  48. TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
  49. class ReplaceAWithB : public SourceChangeRefactoringRule {
  50. std::pair<SourceRange, int> Selection;
  51. public:
  52. ReplaceAWithB(std::pair<SourceRange, int> Selection)
  53. : Selection(Selection) {}
  54. static Expected<ReplaceAWithB>
  55. initiate(RefactoringRuleContext &Cotnext,
  56. std::pair<SourceRange, int> Selection) {
  57. return ReplaceAWithB(Selection);
  58. }
  59. Expected<AtomicChanges>
  60. createSourceReplacements(RefactoringRuleContext &Context) {
  61. const SourceManager &SM = Context.getSources();
  62. SourceLocation Loc =
  63. Selection.first.getBegin().getLocWithOffset(Selection.second);
  64. AtomicChange Change(SM, Loc);
  65. llvm::Error E = Change.replace(SM, Loc, 1, "b");
  66. if (E)
  67. return std::move(E);
  68. return AtomicChanges{Change};
  69. }
  70. };
  71. class SelectionRequirement : public SourceRangeSelectionRequirement {
  72. public:
  73. Expected<std::pair<SourceRange, int>>
  74. evaluate(RefactoringRuleContext &Context) const {
  75. Expected<SourceRange> R =
  76. SourceRangeSelectionRequirement::evaluate(Context);
  77. if (!R)
  78. return R.takeError();
  79. return std::make_pair(*R, 20);
  80. }
  81. };
  82. auto Rule =
  83. createRefactoringActionRule<ReplaceAWithB>(SelectionRequirement());
  84. // When the requirements are satisfied, the rule's function must be invoked.
  85. {
  86. RefactoringRuleContext RefContext(Context.Sources);
  87. SourceLocation Cursor =
  88. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID())
  89. .getLocWithOffset(10);
  90. RefContext.setSelectionRange({Cursor, Cursor});
  91. Expected<AtomicChanges> ErrorOrResult =
  92. createReplacements(Rule, RefContext);
  93. ASSERT_FALSE(!ErrorOrResult);
  94. AtomicChanges Result = std::move(*ErrorOrResult);
  95. ASSERT_EQ(Result.size(), 1u);
  96. std::string YAMLString =
  97. const_cast<AtomicChange &>(Result[0]).toYAMLString();
  98. ASSERT_STREQ("---\n"
  99. "Key: 'input.cpp:30'\n"
  100. "FilePath: input.cpp\n"
  101. "Error: ''\n"
  102. "InsertedHeaders: []\n"
  103. "RemovedHeaders: []\n"
  104. "Replacements:\n"
  105. " - FilePath: input.cpp\n"
  106. " Offset: 30\n"
  107. " Length: 1\n"
  108. " ReplacementText: b\n"
  109. "...\n",
  110. YAMLString.c_str());
  111. }
  112. // When one of the requirements is not satisfied, invoke should return a
  113. // valid error.
  114. {
  115. RefactoringRuleContext RefContext(Context.Sources);
  116. Expected<AtomicChanges> ErrorOrResult =
  117. createReplacements(Rule, RefContext);
  118. ASSERT_TRUE(!ErrorOrResult);
  119. unsigned DiagID;
  120. llvm::handleAllErrors(ErrorOrResult.takeError(),
  121. [&](DiagnosticError &Error) {
  122. DiagID = Error.getDiagnostic().second.getDiagID();
  123. });
  124. EXPECT_EQ(DiagID, diag::err_refactor_no_selection);
  125. }
  126. }
  127. TEST_F(RefactoringActionRulesTest, ReturnError) {
  128. class ErrorRule : public SourceChangeRefactoringRule {
  129. public:
  130. static Expected<ErrorRule> initiate(RefactoringRuleContext &,
  131. SourceRange R) {
  132. return ErrorRule(R);
  133. }
  134. ErrorRule(SourceRange R) {}
  135. Expected<AtomicChanges> createSourceReplacements(RefactoringRuleContext &) {
  136. return llvm::make_error<llvm::StringError>(
  137. "Error", llvm::make_error_code(llvm::errc::invalid_argument));
  138. }
  139. };
  140. auto Rule =
  141. createRefactoringActionRule<ErrorRule>(SourceRangeSelectionRequirement());
  142. RefactoringRuleContext RefContext(Context.Sources);
  143. SourceLocation Cursor =
  144. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
  145. RefContext.setSelectionRange({Cursor, Cursor});
  146. Expected<AtomicChanges> Result = createReplacements(Rule, RefContext);
  147. ASSERT_TRUE(!Result);
  148. std::string Message;
  149. llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
  150. Message = Error.getMessage();
  151. });
  152. EXPECT_EQ(Message, "Error");
  153. }
  154. Optional<SymbolOccurrences> findOccurrences(RefactoringActionRule &Rule,
  155. RefactoringRuleContext &Context) {
  156. class Consumer final : public RefactoringResultConsumer {
  157. void handleError(llvm::Error) override {}
  158. void handle(SymbolOccurrences Occurrences) override {
  159. Result = std::move(Occurrences);
  160. }
  161. void handle(AtomicChanges Changes) override {
  162. RefactoringResultConsumer::handle(std::move(Changes));
  163. }
  164. public:
  165. Optional<SymbolOccurrences> Result;
  166. };
  167. Consumer C;
  168. Rule.invoke(C, Context);
  169. return std::move(C.Result);
  170. }
  171. TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) {
  172. class FindOccurrences : public FindSymbolOccurrencesRefactoringRule {
  173. SourceRange Selection;
  174. public:
  175. FindOccurrences(SourceRange Selection) : Selection(Selection) {}
  176. static Expected<FindOccurrences> initiate(RefactoringRuleContext &,
  177. SourceRange Selection) {
  178. return FindOccurrences(Selection);
  179. }
  180. Expected<SymbolOccurrences>
  181. findSymbolOccurrences(RefactoringRuleContext &) override {
  182. SymbolOccurrences Occurrences;
  183. Occurrences.push_back(SymbolOccurrence(SymbolName("test"),
  184. SymbolOccurrence::MatchingSymbol,
  185. Selection.getBegin()));
  186. return std::move(Occurrences);
  187. }
  188. };
  189. auto Rule = createRefactoringActionRule<FindOccurrences>(
  190. SourceRangeSelectionRequirement());
  191. RefactoringRuleContext RefContext(Context.Sources);
  192. SourceLocation Cursor =
  193. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
  194. RefContext.setSelectionRange({Cursor, Cursor});
  195. Optional<SymbolOccurrences> Result = findOccurrences(*Rule, RefContext);
  196. ASSERT_FALSE(!Result);
  197. SymbolOccurrences Occurrences = std::move(*Result);
  198. EXPECT_EQ(Occurrences.size(), 1u);
  199. EXPECT_EQ(Occurrences[0].getKind(), SymbolOccurrence::MatchingSymbol);
  200. EXPECT_EQ(Occurrences[0].getNameRanges().size(), 1u);
  201. EXPECT_EQ(Occurrences[0].getNameRanges()[0],
  202. SourceRange(Cursor, Cursor.getLocWithOffset(strlen("test"))));
  203. }
  204. TEST_F(RefactoringActionRulesTest, EditorCommandBinding) {
  205. const RefactoringDescriptor &Descriptor = ExtractFunction::describe();
  206. EXPECT_EQ(Descriptor.Name, "extract-function");
  207. EXPECT_EQ(
  208. Descriptor.Description,
  209. "(WIP action; use with caution!) Extracts code into a new function");
  210. EXPECT_EQ(Descriptor.Title, "Extract Function");
  211. }
  212. } // end anonymous namespace