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