RefactoringActionRulesTest.cpp 8.4 KB

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