1//===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===//
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
9#include "clang/Tooling/Refactoring.h"
10#include "ReplacementTest.h"
11#include "RewriterTestContext.h"
12#include "clang/AST/ASTConsumer.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/DeclCXX.h"
15#include "clang/AST/DeclGroup.h"
16#include "clang/AST/RecursiveASTVisitor.h"
17#include "clang/Basic/Diagnostic.h"
18#include "clang/Basic/DiagnosticOptions.h"
19#include "clang/Basic/FileManager.h"
20#include "clang/Basic/LangOptions.h"
21#include "clang/Basic/SourceManager.h"
22#include "clang/Format/Format.h"
23#include "clang/Frontend/CompilerInstance.h"
24#include "clang/Frontend/FrontendAction.h"
25#include "clang/Frontend/TextDiagnosticPrinter.h"
26#include "clang/Rewrite/Core/Rewriter.h"
27#include "clang/Tooling/Refactoring/AtomicChange.h"
28#include "clang/Tooling/Tooling.h"
29#include "llvm/ADT/SmallString.h"
30#include "llvm/Support/VirtualFileSystem.h"
31#include "gtest/gtest.h"
32#include <optional>
33
34namespace clang {
35namespace tooling {
36
37TEST_F(ReplacementTest, CanDeleteAllText) {
38 FileID ID = Context.createInMemoryFile(Name: "input.cpp", Content: "text");
39 SourceLocation Location = Context.getLocation(ID, Line: 1, Column: 1);
40 Replacement Replace(createReplacement(Start: Location, Length: 4, ReplacementText: ""));
41 EXPECT_TRUE(Replace.apply(Context.Rewrite));
42 EXPECT_EQ("", Context.getRewrittenText(ID));
43}
44
45TEST_F(ReplacementTest, CanDeleteAllTextInTextWithNewlines) {
46 FileID ID = Context.createInMemoryFile(Name: "input.cpp", Content: "line1\nline2\nline3");
47 SourceLocation Location = Context.getLocation(ID, Line: 1, Column: 1);
48 Replacement Replace(createReplacement(Start: Location, Length: 17, ReplacementText: ""));
49 EXPECT_TRUE(Replace.apply(Context.Rewrite));
50 EXPECT_EQ("", Context.getRewrittenText(ID));
51}
52
53TEST_F(ReplacementTest, CanAddText) {
54 FileID ID = Context.createInMemoryFile(Name: "input.cpp", Content: "");
55 SourceLocation Location = Context.getLocation(ID, Line: 1, Column: 1);
56 Replacement Replace(createReplacement(Start: Location, Length: 0, ReplacementText: "result"));
57 EXPECT_TRUE(Replace.apply(Context.Rewrite));
58 EXPECT_EQ("result", Context.getRewrittenText(ID));
59}
60
61TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
62 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
63 Content: "line1\nline2\nline3\nline4");
64 SourceLocation Location = Context.getLocation(ID, Line: 2, Column: 3);
65 Replacement Replace(createReplacement(Start: Location, Length: 12, ReplacementText: "x"));
66 EXPECT_TRUE(Replace.apply(Context.Rewrite));
67 EXPECT_EQ("line1\nlixne4", Context.getRewrittenText(ID));
68}
69
70TEST_F(ReplacementTest, CanReplaceTextAtPositionMultipleTimes) {
71 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
72 Content: "line1\nline2\nline3\nline4");
73 SourceLocation Location1 = Context.getLocation(ID, Line: 2, Column: 3);
74 Replacement Replace1(createReplacement(Start: Location1, Length: 12, ReplacementText: "x\ny\n"));
75 EXPECT_TRUE(Replace1.apply(Context.Rewrite));
76 EXPECT_EQ("line1\nlix\ny\nne4", Context.getRewrittenText(ID));
77
78 // Since the original source has not been modified, the (4, 4) points to the
79 // 'e' in the original content.
80 SourceLocation Location2 = Context.getLocation(ID, Line: 4, Column: 4);
81 Replacement Replace2(createReplacement(Start: Location2, Length: 1, ReplacementText: "f"));
82 EXPECT_TRUE(Replace2.apply(Context.Rewrite));
83 EXPECT_EQ("line1\nlix\ny\nnf4", Context.getRewrittenText(ID));
84}
85
86TEST_F(ReplacementTest, ApplyFailsForNonExistentLocation) {
87 Replacement Replace("nonexistent-file.cpp", 0, 1, "");
88 EXPECT_FALSE(Replace.apply(Context.Rewrite));
89}
90
91TEST_F(ReplacementTest, CanRetrivePath) {
92 Replacement Replace("/path/to/file.cpp", 0, 1, "");
93 EXPECT_EQ("/path/to/file.cpp", Replace.getFilePath());
94}
95
96TEST_F(ReplacementTest, ReturnsInvalidPath) {
97 Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
98 EXPECT_TRUE(Replace1.getFilePath().empty());
99
100 Replacement Replace2;
101 EXPECT_TRUE(Replace2.getFilePath().empty());
102}
103
104// Checks that an llvm::Error instance contains a ReplacementError with expected
105// error code, expected new replacement, and expected existing replacement.
106static bool checkReplacementError(llvm::Error &&Error,
107 replacement_error ExpectedErr,
108 std::optional<Replacement> ExpectedExisting,
109 std::optional<Replacement> ExpectedNew) {
110 if (!Error) {
111 llvm::errs() << "Error is a success.";
112 return false;
113 }
114 std::string ErrorMessage;
115 llvm::raw_string_ostream OS(ErrorMessage);
116 llvm::handleAllErrors(E: std::move(Error), Handlers: [&](const ReplacementError &RE) {
117 llvm::errs() << "Handling error...\n";
118 if (ExpectedErr != RE.get())
119 OS << "Unexpected error code: " << int(RE.get()) << "\n";
120 if (ExpectedExisting != RE.getExistingReplacement()) {
121 OS << "Expected Existing != Actual Existing.\n";
122 if (ExpectedExisting)
123 OS << "Expected existing replacement: " << ExpectedExisting->toString()
124 << "\n";
125 if (RE.getExistingReplacement())
126 OS << "Actual existing replacement: "
127 << RE.getExistingReplacement()->toString() << "\n";
128 }
129 if (ExpectedNew != RE.getNewReplacement()) {
130 OS << "Expected New != Actual New.\n";
131 if (ExpectedNew)
132 OS << "Expected new replacement: " << ExpectedNew->toString() << "\n";
133 if (RE.getNewReplacement())
134 OS << "Actual new replacement: " << RE.getNewReplacement()->toString()
135 << "\n";
136 }
137 });
138 OS.flush();
139 if (ErrorMessage.empty()) return true;
140 llvm::errs() << ErrorMessage;
141 return false;
142}
143
144TEST_F(ReplacementTest, FailAddReplacements) {
145 Replacements Replaces;
146 Replacement Deletion("x.cc", 0, 10, "3");
147 auto Err = Replaces.add(R: Deletion);
148 EXPECT_TRUE(!Err);
149 llvm::consumeError(Err: std::move(Err));
150
151 Replacement OverlappingReplacement("x.cc", 0, 2, "a");
152 Err = Replaces.add(R: OverlappingReplacement);
153 EXPECT_TRUE(checkReplacementError(std::move(Err),
154 replacement_error::overlap_conflict,
155 Deletion, OverlappingReplacement));
156
157 Replacement ContainedReplacement("x.cc", 2, 2, "a");
158 Err = Replaces.add(R: Replacement(ContainedReplacement));
159 EXPECT_TRUE(checkReplacementError(std::move(Err),
160 replacement_error::overlap_conflict,
161 Deletion, ContainedReplacement));
162
163 Replacement WrongPathReplacement("y.cc", 20, 2, "");
164 Err = Replaces.add(R: WrongPathReplacement);
165 EXPECT_TRUE(checkReplacementError(std::move(Err),
166 replacement_error::wrong_file_path,
167 Deletion, WrongPathReplacement));
168
169 EXPECT_EQ(1u, Replaces.size());
170 EXPECT_EQ(Deletion, *Replaces.begin());
171}
172
173TEST_F(ReplacementTest, DeletionInReplacements) {
174 Replacements Replaces;
175 Replacement R("x.cc", 0, 10, "3");
176 auto Err = Replaces.add(R);
177 EXPECT_TRUE(!Err);
178 llvm::consumeError(Err: std::move(Err));
179 Err = Replaces.add(R: Replacement("x.cc", 0, 2, ""));
180 EXPECT_TRUE(!Err);
181 llvm::consumeError(Err: std::move(Err));
182 Err = Replaces.add(R: Replacement("x.cc", 2, 2, ""));
183 EXPECT_TRUE(!Err);
184 llvm::consumeError(Err: std::move(Err));
185 EXPECT_EQ(1u, Replaces.size());
186 EXPECT_EQ(R, *Replaces.begin());
187}
188
189TEST_F(ReplacementTest, OverlappingReplacements) {
190 Replacements Replaces;
191 auto Err = Replaces.add(R: Replacement("x.cc", 0, 3, "345"));
192 EXPECT_TRUE(!Err);
193 llvm::consumeError(Err: std::move(Err));
194 Err = Replaces.add(R: Replacement("x.cc", 2, 3, "543"));
195 EXPECT_TRUE(!Err);
196 llvm::consumeError(Err: std::move(Err));
197
198 EXPECT_EQ(1u, Replaces.size());
199 EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin());
200
201 Err = Replaces.add(R: Replacement("x.cc", 2, 1, "5"));
202 EXPECT_TRUE(!Err);
203 llvm::consumeError(Err: std::move(Err));
204 EXPECT_EQ(1u, Replaces.size());
205 EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin());
206}
207
208TEST_F(ReplacementTest, AddAdjacentInsertionAndReplacement) {
209 Replacements Replaces;
210 // Test adding an insertion at the offset of an existing replacement.
211 auto Err = Replaces.add(R: Replacement("x.cc", 10, 3, "replace"));
212 EXPECT_TRUE(!Err);
213 llvm::consumeError(Err: std::move(Err));
214 Err = Replaces.add(R: Replacement("x.cc", 10, 0, "insert"));
215 EXPECT_TRUE(!Err);
216 llvm::consumeError(Err: std::move(Err));
217 EXPECT_EQ(Replaces.size(), 2u);
218
219 Replaces.clear();
220 // Test overlap with an existing insertion.
221 Err = Replaces.add(R: Replacement("x.cc", 10, 0, "insert"));
222 EXPECT_TRUE(!Err);
223 llvm::consumeError(Err: std::move(Err));
224 Err = Replaces.add(R: Replacement("x.cc", 10, 3, "replace"));
225 EXPECT_TRUE(!Err);
226 llvm::consumeError(Err: std::move(Err));
227 EXPECT_EQ(Replaces.size(), 2u);
228}
229
230TEST_F(ReplacementTest, MergeNewDeletions) {
231 Replacements Replaces;
232 Replacement ContainingReplacement("x.cc", 0, 10, "");
233 auto Err = Replaces.add(R: ContainingReplacement);
234 EXPECT_TRUE(!Err);
235 llvm::consumeError(Err: std::move(Err));
236
237 Err = Replaces.add(R: Replacement("x.cc", 5, 3, ""));
238 EXPECT_TRUE(!Err);
239 llvm::consumeError(Err: std::move(Err));
240
241 Err = Replaces.add(R: Replacement("x.cc", 0, 10, ""));
242 EXPECT_TRUE(!Err);
243 llvm::consumeError(Err: std::move(Err));
244
245 Err = Replaces.add(R: Replacement("x.cc", 5, 5, ""));
246 EXPECT_TRUE(!Err);
247 llvm::consumeError(Err: std::move(Err));
248
249 EXPECT_EQ(1u, Replaces.size());
250 EXPECT_EQ(*Replaces.begin(), ContainingReplacement);
251}
252
253TEST_F(ReplacementTest, MergeOverlappingButNotAdjacentReplacement) {
254 Replacements Replaces;
255 auto Err = Replaces.add(R: Replacement("x.cc", 0, 2, ""));
256 EXPECT_TRUE(!Err);
257 llvm::consumeError(Err: std::move(Err));
258
259 Err = Replaces.add(R: Replacement("x.cc", 5, 5, ""));
260 EXPECT_TRUE(!Err);
261 llvm::consumeError(Err: std::move(Err));
262
263 Replacement After = Replacement("x.cc", 10, 5, "");
264 Err = Replaces.add(R: After);
265 EXPECT_TRUE(!Err);
266 llvm::consumeError(Err: std::move(Err));
267
268 Replacement ContainingReplacement("x.cc", 0, 10, "");
269 Err = Replaces.add(R: ContainingReplacement);
270 EXPECT_TRUE(!Err);
271 llvm::consumeError(Err: std::move(Err));
272
273 EXPECT_EQ(2u, Replaces.size());
274 EXPECT_EQ(*Replaces.begin(), ContainingReplacement);
275 EXPECT_EQ(*(++Replaces.begin()), After);
276}
277
278TEST_F(ReplacementTest, InsertionBeforeMergedDeletions) {
279 Replacements Replaces;
280
281 Replacement Insertion("x.cc", 0, 0, "123");
282 auto Err = Replaces.add(R: Insertion);
283 EXPECT_TRUE(!Err);
284 llvm::consumeError(Err: std::move(Err));
285
286 Err = Replaces.add(R: Replacement("x.cc", 5, 5, ""));
287 EXPECT_TRUE(!Err);
288 llvm::consumeError(Err: std::move(Err));
289
290 Replacement Deletion("x.cc", 0, 10, "");
291 Err = Replaces.add(R: Deletion);
292 EXPECT_TRUE(!Err);
293 llvm::consumeError(Err: std::move(Err));
294
295 EXPECT_EQ(2u, Replaces.size());
296 EXPECT_EQ(*Replaces.begin(), Insertion);
297 EXPECT_EQ(*(++Replaces.begin()), Deletion);
298}
299
300TEST_F(ReplacementTest, MergeOverlappingDeletions) {
301 Replacements Replaces;
302 auto Err = Replaces.add(R: Replacement("x.cc", 0, 2, ""));
303 EXPECT_TRUE(!Err);
304 llvm::consumeError(Err: std::move(Err));
305
306 Err = Replaces.add(R: Replacement("x.cc", 0, 5, ""));
307 EXPECT_TRUE(!Err);
308 llvm::consumeError(Err: std::move(Err));
309
310 EXPECT_EQ(1u, Replaces.size());
311 EXPECT_EQ(Replacement("x.cc", 0, 5, ""), *Replaces.begin());
312
313 Err = Replaces.add(R: Replacement("x.cc", 1, 5, ""));
314 EXPECT_TRUE(!Err);
315 llvm::consumeError(Err: std::move(Err));
316 EXPECT_EQ(1u, Replaces.size());
317 EXPECT_EQ(Replacement("x.cc", 0, 6, ""), *Replaces.begin());
318}
319
320TEST_F(ReplacementTest, FailedMergeExistingDeletions) {
321 Replacements Replaces;
322 Replacement First("x.cc", 0, 2, "");
323 auto Err = Replaces.add(R: First);
324 EXPECT_TRUE(!Err);
325 llvm::consumeError(Err: std::move(Err));
326
327 Replacement Second("x.cc", 5, 5, "");
328 Err = Replaces.add(R: Second);
329 EXPECT_TRUE(!Err);
330 llvm::consumeError(Err: std::move(Err));
331
332 Err = Replaces.add(R: Replacement("x.cc", 1, 10, ""));
333 EXPECT_TRUE(!Err);
334 llvm::consumeError(Err: std::move(Err));
335
336 EXPECT_EQ(1u, Replaces.size());
337 EXPECT_EQ(Replacement("x.cc", 0, 11, ""), *Replaces.begin());
338}
339
340TEST_F(ReplacementTest, FailAddRegression) {
341 Replacements Replaces;
342 // Create two replacements, where the second one is an insertion of the empty
343 // string exactly at the end of the first one.
344 auto Err = Replaces.add(R: Replacement("x.cc", 0, 10, "1"));
345 EXPECT_TRUE(!Err);
346 llvm::consumeError(Err: std::move(Err));
347 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
348 EXPECT_TRUE(!Err);
349 llvm::consumeError(Err: std::move(Err));
350
351 // Make sure we find the overlap with the first entry when inserting a
352 // replacement that ends exactly at the seam of the existing replacements.
353 Replacement OverlappingReplacement("x.cc", 5, 5, "fail");
354 Err = Replaces.add(R: OverlappingReplacement);
355 EXPECT_TRUE(checkReplacementError(std::move(Err),
356 replacement_error::overlap_conflict,
357 *Replaces.begin(), OverlappingReplacement));
358
359 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
360 EXPECT_TRUE(!Err);
361 llvm::consumeError(Err: std::move(Err));
362}
363
364TEST_F(ReplacementTest, InsertAtOffsetOfReplacement) {
365 Replacements Replaces;
366 auto Err = Replaces.add(R: Replacement("x.cc", 10, 2, ""));
367 EXPECT_TRUE(!Err);
368 llvm::consumeError(Err: std::move(Err));
369 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
370 EXPECT_TRUE(!Err);
371 llvm::consumeError(Err: std::move(Err));
372 EXPECT_EQ(Replaces.size(), 2u);
373
374 Replaces.clear();
375 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
376 EXPECT_TRUE(!Err);
377 llvm::consumeError(Err: std::move(Err));
378 Err = Replaces.add(R: Replacement("x.cc", 10, 2, ""));
379 EXPECT_TRUE(!Err);
380 llvm::consumeError(Err: std::move(Err));
381 EXPECT_EQ(Replaces.size(), 2u);
382}
383
384TEST_F(ReplacementTest, AddInsertAtOtherInsertWhenOderIndependent) {
385 Replacements Replaces;
386 auto Err = Replaces.add(R: Replacement("x.cc", 10, 0, "a"));
387 EXPECT_TRUE(!Err);
388 llvm::consumeError(Err: std::move(Err));
389 Replacement ConflictInsertion("x.cc", 10, 0, "b");
390 Err = Replaces.add(R: ConflictInsertion);
391 EXPECT_TRUE(checkReplacementError(std::move(Err),
392 replacement_error::insert_conflict,
393 *Replaces.begin(), ConflictInsertion));
394
395 Replaces.clear();
396 Err = Replaces.add(R: Replacement("x.cc", 10, 0, "a"));
397 EXPECT_TRUE(!Err);
398 llvm::consumeError(Err: std::move(Err));
399 Err = Replaces.add(R: Replacement("x.cc", 10, 0, "aa"));
400 EXPECT_TRUE(!Err);
401 llvm::consumeError(Err: std::move(Err));
402 EXPECT_EQ(1u, Replaces.size());
403 EXPECT_EQ(Replacement("x.cc", 10, 0, "aaa"), *Replaces.begin());
404
405 Replaces.clear();
406 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
407 EXPECT_TRUE(!Err);
408 llvm::consumeError(Err: std::move(Err));
409 Err = Replaces.add(R: Replacement("x.cc", 10, 3, ""));
410 EXPECT_TRUE(!Err);
411 llvm::consumeError(Err: std::move(Err));
412 Err = Replaces.add(R: Replacement("x.cc", 10, 0, ""));
413 EXPECT_TRUE(!Err);
414 llvm::consumeError(Err: std::move(Err));
415 EXPECT_EQ(2u, Replaces.size());
416 EXPECT_EQ(Replacement("x.cc", 10, 0, ""), *Replaces.begin());
417 EXPECT_EQ(Replacement("x.cc", 10, 3, ""), *std::next(Replaces.begin()));
418}
419
420TEST_F(ReplacementTest, InsertBetweenAdjacentReplacements) {
421 Replacements Replaces;
422 auto Err = Replaces.add(R: Replacement("x.cc", 10, 5, "a"));
423 EXPECT_TRUE(!Err);
424 llvm::consumeError(Err: std::move(Err));
425 Err = Replaces.add(R: Replacement("x.cc", 8, 2, "a"));
426 EXPECT_TRUE(!Err);
427 llvm::consumeError(Err: std::move(Err));
428 Err = Replaces.add(R: Replacement("x.cc", 10, 0, "b"));
429 EXPECT_TRUE(!Err);
430 llvm::consumeError(Err: std::move(Err));
431}
432
433TEST_F(ReplacementTest, CanApplyReplacements) {
434 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
435 Content: "line1\nline2\nline3\nline4");
436 Replacements Replaces =
437 toReplacements(Replaces: {Replacement(Context.Sources,
438 Context.getLocation(ID, Line: 2, Column: 1), 5, "replaced"),
439 Replacement(Context.Sources,
440 Context.getLocation(ID, Line: 3, Column: 1), 5, "other")});
441 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
442 EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
443}
444
445// Verifies that replacement/deletion is applied before insertion at the same
446// offset.
447TEST_F(ReplacementTest, InsertAndDelete) {
448 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
449 Content: "line1\nline2\nline3\nline4");
450 Replacements Replaces = toReplacements(
451 Replaces: {Replacement(Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 6, ""),
452 Replacement(Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 0,
453 "other\n")});
454 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
455 EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID));
456}
457
458TEST_F(ReplacementTest, AdjacentReplacements) {
459 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
460 Content: "ab");
461 Replacements Replaces = toReplacements(
462 Replaces: {Replacement(Context.Sources, Context.getLocation(ID, Line: 1, Column: 1), 1, "x"),
463 Replacement(Context.Sources, Context.getLocation(ID, Line: 1, Column: 2), 1, "y")});
464 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
465 EXPECT_EQ("xy", Context.getRewrittenText(ID));
466}
467
468TEST_F(ReplacementTest, AddDuplicateReplacements) {
469 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
470 Content: "line1\nline2\nline3\nline4");
471 auto Replaces = toReplacements(Replaces: {Replacement(
472 Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 5, "replaced")});
473
474 auto Err = Replaces.add(R: Replacement(
475 Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 5, "replaced"));
476 EXPECT_TRUE(!Err);
477 llvm::consumeError(Err: std::move(Err));
478
479 Err = Replaces.add(R: Replacement(Context.Sources, Context.getLocation(ID, Line: 2, Column: 1),
480 5, "replaced"));
481 EXPECT_TRUE(!Err);
482 llvm::consumeError(Err: std::move(Err));
483
484 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
485 EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
486}
487
488TEST_F(ReplacementTest, FailOrderDependentReplacements) {
489 FileID ID = Context.createInMemoryFile(Name: "input.cpp",
490 Content: "line1\nline2\nline3\nline4");
491 auto Replaces = toReplacements(Replaces: {Replacement(
492 Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 5, "other")});
493
494 Replacement ConflictReplacement(Context.Sources,
495 Context.getLocation(ID, Line: 2, Column: 1), 5, "rehto");
496 auto Err = Replaces.add(R: ConflictReplacement);
497 EXPECT_TRUE(checkReplacementError(std::move(Err),
498 replacement_error::overlap_conflict,
499 *Replaces.begin(), ConflictReplacement));
500
501 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
502 EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID));
503}
504
505TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) {
506 Replacements Replaces =
507 toReplacements(Replaces: {Replacement(Context.Sources, SourceLocation(), 5, "2")});
508
509 EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
510}
511
512TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
513 // Column limit is 20.
514 std::string Code1 = "Long *a =\n"
515 " new Long();\n"
516 "long x = 1;";
517 std::string Expected1 = "auto a = new Long();\n"
518 "long x =\n"
519 " 12345678901;";
520 std::string Code2 = "int x = 123;\n"
521 "int y = 0;";
522 std::string Expected2 = "int x =\n"
523 " 1234567890123;\n"
524 "int y = 10;";
525 StringRef File1 = "format_1.cpp";
526 StringRef File2 = "format_2.cpp";
527 FileID ID1 = Context.createInMemoryFile(Name: File1, Content: Code1);
528 FileID ID2 = Context.createInMemoryFile(Name: File2, Content: Code2);
529
530 // Scrambled the order of replacements.
531 std::map<std::string, Replacements> FileToReplaces;
532 FileToReplaces[std::string(File1)] = toReplacements(
533 Replaces: {tooling::Replacement(Context.Sources, Context.getLocation(ID: ID1, Line: 1, Column: 1), 6,
534 "auto "),
535 tooling::Replacement(Context.Sources, Context.getLocation(ID: ID1, Line: 3, Column: 10), 1,
536 "12345678901")});
537 FileToReplaces[std::string(File2)] = toReplacements(
538 Replaces: {tooling::Replacement(Context.Sources, Context.getLocation(ID: ID2, Line: 1, Column: 12), 0,
539 "4567890123"),
540 tooling::Replacement(Context.Sources, Context.getLocation(ID: ID2, Line: 2, Column: 9), 1,
541 "10")});
542 EXPECT_TRUE(
543 formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite,
544 "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
545 EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
546 EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
547}
548
549TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
550 Replacements Replaces =
551 toReplacements(Replaces: {Replacement("", 0, 1, ""), Replacement("", 4, 3, " ")});
552 // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
553 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int i;
554 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); // |nt i;
555 EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); // i|t i;
556 EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); // in| i;
557 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); // int| i;
558 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); // int | i;
559 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); // int |i;
560 EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); // int |;
561 EXPECT_EQ(5u, Replaces.getShiftedCodePosition(8)); // int i|
562}
563
564TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
565 Replacements Replaces = toReplacements(Replaces: {Replacement("", 4, 0, "\"\n\"")});
566 // Assume '"12345678"' is turned into '"1234"\n"5678"'.
567 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678"
568 EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678"
569 EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78"
570}
571
572TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
573 // Replace the first four characters with "abcd".
574 auto Replaces = toReplacements(Replaces: {Replacement("", 0, 4, "abcd")});
575 for (unsigned i = 0; i < 3; ++i)
576 EXPECT_EQ(i, Replaces.getShiftedCodePosition(i));
577}
578
579TEST(ShiftedCodePositionTest, NoReplacementText) {
580 Replacements Replaces = toReplacements(Replaces: {Replacement("", 0, 42, "")});
581 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0));
582 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(39));
583 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(45));
584 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(42));
585}
586
587class FlushRewrittenFilesTest : public ::testing::Test {
588public:
589 FlushRewrittenFilesTest() {}
590
591 ~FlushRewrittenFilesTest() override {
592 for (llvm::StringMap<std::string>::iterator I = TemporaryFiles.begin(),
593 E = TemporaryFiles.end();
594 I != E; ++I) {
595 llvm::StringRef Name = I->second;
596 std::error_code EC = llvm::sys::fs::remove(path: Name);
597 (void)EC;
598 assert(!EC);
599 }
600 }
601
602 FileID createFile(llvm::StringRef Name, llvm::StringRef Content) {
603 SmallString<1024> Path;
604 int FD;
605 std::error_code EC = llvm::sys::fs::createTemporaryFile(Prefix: Name, Suffix: "", ResultFD&: FD, ResultPath&: Path);
606 assert(!EC);
607 (void)EC;
608
609 llvm::raw_fd_ostream OutStream(FD, true);
610 OutStream << Content;
611 OutStream.close();
612 auto File = Context.Files.getOptionalFileRef(Filename: Path);
613 assert(File);
614
615 StringRef Found =
616 TemporaryFiles.insert(KV: std::make_pair(x&: Name, y: std::string(Path.str())))
617 .first->second;
618 assert(Found == Path);
619 (void)Found;
620 return Context.Sources.createFileID(SourceFile: *File, IncludePos: SourceLocation(),
621 FileCharacter: SrcMgr::C_User);
622 }
623
624 std::string getFileContentFromDisk(llvm::StringRef Name) {
625 std::string Path = TemporaryFiles.lookup(Key: Name);
626 assert(!Path.empty());
627 // We need to read directly from the FileManager without relaying through
628 // a FileEntry, as otherwise we'd read through an already opened file
629 // descriptor, which might not see the changes made.
630 // FIXME: Figure out whether there is a way to get the SourceManger to
631 // reopen the file.
632 auto FileBuffer = Context.Files.getBufferForFile(Filename: Path);
633 return std::string((*FileBuffer)->getBuffer());
634 }
635
636 llvm::StringMap<std::string> TemporaryFiles;
637 RewriterTestContext Context;
638};
639
640TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
641 FileID ID = createFile(Name: "input.cpp", Content: "line1\nline2\nline3\nline4");
642 Replacements Replaces = toReplacements(Replaces: {Replacement(
643 Context.Sources, Context.getLocation(ID, Line: 2, Column: 1), 5, "replaced")});
644 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
645 EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
646 EXPECT_EQ("line1\nreplaced\nline3\nline4",
647 getFileContentFromDisk("input.cpp"));
648}
649
650namespace {
651template <typename T>
652class TestVisitor : public clang::RecursiveASTVisitor<T> {
653public:
654 bool runOver(StringRef Code) {
655 return runToolOnCode(std::make_unique<TestAction>(this), Code);
656 }
657
658protected:
659 clang::SourceManager *SM;
660 clang::ASTContext *Context;
661
662private:
663 class FindConsumer : public clang::ASTConsumer {
664 public:
665 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
666
667 void HandleTranslationUnit(clang::ASTContext &Context) override {
668 Visitor->TraverseDecl(Context.getTranslationUnitDecl());
669 }
670
671 private:
672 TestVisitor *Visitor;
673 };
674
675 class TestAction : public clang::ASTFrontendAction {
676 public:
677 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
678
679 std::unique_ptr<clang::ASTConsumer>
680 CreateASTConsumer(clang::CompilerInstance &compiler,
681 llvm::StringRef dummy) override {
682 Visitor->SM = &compiler.getSourceManager();
683 Visitor->Context = &compiler.getASTContext();
684 /// TestConsumer will be deleted by the framework calling us.
685 return std::make_unique<FindConsumer>(Visitor);
686 }
687
688 private:
689 TestVisitor *Visitor;
690 };
691};
692} // end namespace
693
694void expectReplacementAt(const Replacement &Replace,
695 StringRef File, unsigned Offset, unsigned Length) {
696 ASSERT_TRUE(Replace.isApplicable());
697 EXPECT_EQ(File, Replace.getFilePath());
698 EXPECT_EQ(Offset, Replace.getOffset());
699 EXPECT_EQ(Length, Replace.getLength());
700}
701
702class ClassDeclXVisitor : public TestVisitor<ClassDeclXVisitor> {
703public:
704 bool VisitCXXRecordDecl(CXXRecordDecl *Record) {
705 if (Record->getName() == "X") {
706 Replace = Replacement(*SM, Record, "");
707 }
708 return true;
709 }
710 Replacement Replace;
711};
712
713TEST(Replacement, CanBeConstructedFromNode) {
714 ClassDeclXVisitor ClassDeclX;
715 EXPECT_TRUE(ClassDeclX.runOver(" class X;"));
716 expectReplacementAt(Replace: ClassDeclX.Replace, File: "input.cc", Offset: 5, Length: 7);
717}
718
719TEST(Replacement, ReplacesAtSpellingLocation) {
720 ClassDeclXVisitor ClassDeclX;
721 EXPECT_TRUE(ClassDeclX.runOver("#define A(Y) Y\nA(class X);"));
722 expectReplacementAt(Replace: ClassDeclX.Replace, File: "input.cc", Offset: 17, Length: 7);
723}
724
725class CallToFVisitor : public TestVisitor<CallToFVisitor> {
726public:
727 bool VisitCallExpr(CallExpr *Call) {
728 if (Call->getDirectCallee()->getName() == "F") {
729 Replace = Replacement(*SM, Call, "");
730 }
731 return true;
732 }
733 Replacement Replace;
734};
735
736TEST(Replacement, FunctionCall) {
737 CallToFVisitor CallToF;
738 EXPECT_TRUE(CallToF.runOver("void F(); void G() { F(); }"));
739 expectReplacementAt(Replace: CallToF.Replace, File: "input.cc", Offset: 21, Length: 3);
740}
741
742TEST(Replacement, TemplatedFunctionCall) {
743 CallToFVisitor CallToF;
744 EXPECT_TRUE(CallToF.runOver(
745 "template <typename T> void F(); void G() { F<int>(); }"));
746 expectReplacementAt(Replace: CallToF.Replace, File: "input.cc", Offset: 43, Length: 8);
747}
748
749class NestedNameSpecifierAVisitor
750 : public TestVisitor<NestedNameSpecifierAVisitor> {
751public:
752 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLoc) {
753 if (NNSLoc.getNestedNameSpecifier()) {
754 if (const NamespaceDecl* NS = NNSLoc.getNestedNameSpecifier()->getAsNamespace()) {
755 if (NS->getName() == "a") {
756 Replace = Replacement(*SM, &NNSLoc, "", Context->getLangOpts());
757 }
758 }
759 }
760 return TestVisitor<NestedNameSpecifierAVisitor>::TraverseNestedNameSpecifierLoc(
761 NNS: NNSLoc);
762 }
763 Replacement Replace;
764};
765
766TEST(Replacement, ColonColon) {
767 NestedNameSpecifierAVisitor VisitNNSA;
768 EXPECT_TRUE(VisitNNSA.runOver("namespace a { void f() { ::a::f(); } }"));
769 expectReplacementAt(Replace: VisitNNSA.Replace, File: "input.cc", Offset: 25, Length: 5);
770}
771
772TEST(Range, overlaps) {
773 EXPECT_TRUE(Range(10, 10).overlapsWith(Range(0, 11)));
774 EXPECT_TRUE(Range(0, 11).overlapsWith(Range(10, 10)));
775 EXPECT_FALSE(Range(10, 10).overlapsWith(Range(0, 10)));
776 EXPECT_FALSE(Range(0, 10).overlapsWith(Range(10, 10)));
777 EXPECT_TRUE(Range(0, 10).overlapsWith(Range(2, 6)));
778 EXPECT_TRUE(Range(2, 6).overlapsWith(Range(0, 10)));
779}
780
781TEST(Range, contains) {
782 EXPECT_TRUE(Range(0, 10).contains(Range(0, 10)));
783 EXPECT_TRUE(Range(0, 10).contains(Range(2, 6)));
784 EXPECT_FALSE(Range(2, 6).contains(Range(0, 10)));
785 EXPECT_FALSE(Range(0, 10).contains(Range(0, 11)));
786}
787
788TEST(Range, CalculateRangesOfReplacements) {
789 // Before: aaaabbbbbbz
790 // After : bbbbbbzzzzzzoooooooooooooooo
791 Replacements Replaces = toReplacements(
792 Replaces: {Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"),
793 Replacement("foo", 11, 0, "oooooooooooooooo")});
794
795 std::vector<Range> Ranges = Replaces.getAffectedRanges();
796
797 EXPECT_EQ(2ul, Ranges.size());
798 EXPECT_TRUE(Ranges[0].getOffset() == 0);
799 EXPECT_TRUE(Ranges[0].getLength() == 0);
800 EXPECT_TRUE(Ranges[1].getOffset() == 6);
801 EXPECT_TRUE(Ranges[1].getLength() == 22);
802}
803
804TEST(Range, CalculateRangesOfInsertionAroundReplacement) {
805 Replacements Replaces = toReplacements(
806 Replaces: {Replacement("foo", 0, 2, ""), Replacement("foo", 0, 0, "ba")});
807
808 std::vector<Range> Ranges = Replaces.getAffectedRanges();
809
810 EXPECT_EQ(1ul, Ranges.size());
811 EXPECT_EQ(0u, Ranges[0].getOffset());
812 EXPECT_EQ(2u, Ranges[0].getLength());
813}
814
815TEST(Range, RangesAfterEmptyReplacements) {
816 std::vector<Range> Ranges = {Range(5, 6), Range(10, 5)};
817 Replacements Replaces;
818 std::vector<Range> Expected = {Range(5, 10)};
819 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
820}
821
822TEST(Range, RangesAfterReplacements) {
823 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
824 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 0, 2, "1234")});
825 std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
826 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
827}
828
829TEST(Range, RangesBeforeReplacements) {
830 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
831 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 20, 2, "1234")});
832 std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
833 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
834}
835
836TEST(Range, NotAffectedByReplacements) {
837 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
838 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 3, 2, "12"),
839 Replacement("foo", 12, 2, "12"),
840 Replacement("foo", 20, 5, "")});
841 std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
842 Range(20, 0)};
843 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
844}
845
846TEST(Range, RangesWithNonOverlappingReplacements) {
847 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
848 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 3, 1, ""),
849 Replacement("foo", 6, 1, "123"),
850 Replacement("foo", 20, 2, "12345")});
851 std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
852 Range(11, 5), Range(21, 5)};
853 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
854}
855
856TEST(Range, RangesWithOverlappingReplacements) {
857 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
858 Range(30, 5)};
859 Replacements Replaces = toReplacements(
860 Replaces: {Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
861 Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")});
862 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
863 Range(22, 0)};
864 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
865}
866
867TEST(Range, MergeIntoOneRange) {
868 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
869 Replacements Replaces =
870 toReplacements(Replaces: {Replacement("foo", 1, 15, "1234567890")});
871 std::vector<Range> Expected = {Range(0, 15)};
872 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
873}
874
875TEST(Range, ReplacementsStartingAtRangeOffsets) {
876 std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
877 Replacements Replaces = toReplacements(
878 Replaces: {Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
879 Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")});
880 std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
881 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
882}
883
884TEST(Range, ReplacementsEndingAtRangeEnds) {
885 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
886 Replacements Replaces = toReplacements(
887 Replaces: {Replacement("foo", 6, 1, "123"), Replacement("foo", 17, 3, "12")});
888 std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
889 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
890}
891
892TEST(Range, AjacentReplacements) {
893 std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
894 Replacements Replaces = toReplacements(
895 Replaces: {Replacement("foo", 1, 2, "123"), Replacement("foo", 12, 3, "1234")});
896 std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
897 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
898}
899
900TEST(Range, MergeRangesAfterReplacements) {
901 std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
902 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 1, 3, ""),
903 Replacement("foo", 7, 0, "12"),
904 Replacement("foo", 9, 2, "")});
905 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0),
906 Range(8, 0)};
907 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
908}
909
910TEST(Range, ConflictingRangesBeforeReplacements) {
911 std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)};
912 Replacements Replaces = toReplacements(Replaces: {Replacement("foo", 1, 3, "")});
913 std::vector<Range> Expected = {Range(1, 0), Range(2, 6)};
914 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
915}
916
917class MergeReplacementsTest : public ::testing::Test {
918protected:
919 void mergeAndTestRewrite(StringRef Code, StringRef Intermediate,
920 StringRef Result, const Replacements &First,
921 const Replacements &Second) {
922 // These are mainly to verify the test itself and make it easier to read.
923 auto AfterFirst = applyAllReplacements(Code, Replaces: First);
924 EXPECT_TRUE(static_cast<bool>(AfterFirst));
925 auto InSequenceRewrite = applyAllReplacements(Code: *AfterFirst, Replaces: Second);
926 EXPECT_TRUE(static_cast<bool>(InSequenceRewrite));
927 EXPECT_EQ(Intermediate, *AfterFirst);
928 EXPECT_EQ(Result, *InSequenceRewrite);
929
930 tooling::Replacements Merged = First.merge(Replaces: Second);
931 auto MergedRewrite = applyAllReplacements(Code, Replaces: Merged);
932 EXPECT_TRUE(static_cast<bool>(MergedRewrite));
933 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
934 if (*InSequenceRewrite != *MergedRewrite)
935 for (tooling::Replacement M : Merged)
936 llvm::errs() << M.getOffset() << " " << M.getLength() << " "
937 << M.getReplacementText() << "\n";
938 }
939 void mergeAndTestRewrite(StringRef Code, const Replacements &First,
940 const Replacements &Second) {
941 auto AfterFirst = applyAllReplacements(Code, Replaces: First);
942 EXPECT_TRUE(static_cast<bool>(AfterFirst));
943 auto InSequenceRewrite = applyAllReplacements(Code: *AfterFirst, Replaces: Second);
944 tooling::Replacements Merged = First.merge(Replaces: Second);
945 auto MergedRewrite = applyAllReplacements(Code, Replaces: Merged);
946 EXPECT_TRUE(static_cast<bool>(MergedRewrite));
947 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
948 if (*InSequenceRewrite != *MergedRewrite)
949 for (tooling::Replacement M : Merged)
950 llvm::errs() << M.getOffset() << " " << M.getLength() << " "
951 << M.getReplacementText() << "\n";
952 }
953};
954
955TEST_F(MergeReplacementsTest, Offsets) {
956 mergeAndTestRewrite(Code: "aaa", Intermediate: "aabab", Result: "cacabab",
957 First: toReplacements(Replaces: {{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
958 Second: toReplacements(Replaces: {{"", 0, 0, "c"}, {"", 1, 0, "c"}}));
959 mergeAndTestRewrite(Code: "aaa", Intermediate: "babaa", Result: "babacac",
960 First: toReplacements(Replaces: {{"", 0, 0, "b"}, {"", 1, 0, "b"}}),
961 Second: toReplacements(Replaces: {{"", 4, 0, "c"}, {"", 5, 0, "c"}}));
962 mergeAndTestRewrite(Code: "aaaa", Intermediate: "aaa", Result: "aac", First: toReplacements(Replaces: {{"", 1, 1, ""}}),
963 Second: toReplacements(Replaces: {{"", 2, 1, "c"}}));
964
965 mergeAndTestRewrite(Code: "aa", Intermediate: "bbabba", Result: "bbabcba",
966 First: toReplacements(Replaces: {{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}),
967 Second: toReplacements(Replaces: {{"", 4, 0, "c"}}));
968}
969
970TEST_F(MergeReplacementsTest, Concatenations) {
971 // Basic concatenations. It is important to merge these into a single
972 // replacement to ensure the correct order.
973 {
974 auto First = toReplacements(Replaces: {{"", 0, 0, "a"}});
975 auto Second = toReplacements(Replaces: {{"", 1, 0, "b"}});
976 EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second));
977 }
978 {
979 auto First = toReplacements(Replaces: {{"", 0, 0, "a"}});
980 auto Second = toReplacements(Replaces: {{"", 0, 0, "b"}});
981 EXPECT_EQ(toReplacements({{"", 0, 0, "ba"}}), First.merge(Second));
982 }
983 mergeAndTestRewrite(Code: "", Intermediate: "a", Result: "ab", First: toReplacements(Replaces: {{"", 0, 0, "a"}}),
984 Second: toReplacements(Replaces: {{"", 1, 0, "b"}}));
985 mergeAndTestRewrite(Code: "", Intermediate: "a", Result: "ba", First: toReplacements(Replaces: {{"", 0, 0, "a"}}),
986 Second: toReplacements(Replaces: {{"", 0, 0, "b"}}));
987}
988
989TEST_F(MergeReplacementsTest, NotChangingLengths) {
990 mergeAndTestRewrite(Code: "aaaa", Intermediate: "abba", Result: "acca",
991 First: toReplacements(Replaces: {{"", 1, 2, "bb"}}),
992 Second: toReplacements(Replaces: {{"", 1, 2, "cc"}}));
993 mergeAndTestRewrite(Code: "aaaa", Intermediate: "abba", Result: "abcc",
994 First: toReplacements(Replaces: {{"", 1, 2, "bb"}}),
995 Second: toReplacements(Replaces: {{"", 2, 2, "cc"}}));
996 mergeAndTestRewrite(Code: "aaaa", Intermediate: "abba", Result: "ccba",
997 First: toReplacements(Replaces: {{"", 1, 2, "bb"}}),
998 Second: toReplacements(Replaces: {{"", 0, 2, "cc"}}));
999 mergeAndTestRewrite(Code: "aaaaaa", Intermediate: "abbdda", Result: "abccda",
1000 First: toReplacements(Replaces: {{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}),
1001 Second: toReplacements(Replaces: {{"", 2, 2, "cc"}}));
1002}
1003
1004TEST_F(MergeReplacementsTest, OverlappingRanges) {
1005 mergeAndTestRewrite(Code: "aaa", Intermediate: "bbd", Result: "bcbcd",
1006 First: toReplacements(Replaces: {{"", 0, 1, "bb"}, {"", 1, 2, "d"}}),
1007 Second: toReplacements(Replaces: {{"", 1, 0, "c"}, {"", 2, 0, "c"}}));
1008
1009 mergeAndTestRewrite(Code: "aaaa", Intermediate: "aabbaa", Result: "acccca",
1010 First: toReplacements(Replaces: {{"", 2, 0, "bb"}}),
1011 Second: toReplacements(Replaces: {{"", 1, 4, "cccc"}}));
1012 mergeAndTestRewrite(Code: "aaaa", Intermediate: "aababa", Result: "acccca",
1013 First: toReplacements(Replaces: {{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
1014 Second: toReplacements(Replaces: {{"", 1, 4, "cccc"}}));
1015 mergeAndTestRewrite(Code: "aaaaaa", Intermediate: "abbbba", Result: "abba",
1016 First: toReplacements(Replaces: {{"", 1, 4, "bbbb"}}),
1017 Second: toReplacements(Replaces: {{"", 2, 2, ""}}));
1018 mergeAndTestRewrite(Code: "aaaa", Intermediate: "aa", Result: "cc",
1019 First: toReplacements(Replaces: {{"", 1, 1, ""}, {"", 2, 1, ""}}),
1020 Second: toReplacements(Replaces: {{"", 0, 2, "cc"}}));
1021 mergeAndTestRewrite(Code: "aa", Intermediate: "abbba", Result: "abcbcba",
1022 First: toReplacements(Replaces: {{"", 1, 0, "bbb"}}),
1023 Second: toReplacements(Replaces: {{"", 2, 0, "c"}, {"", 3, 0, "c"}}));
1024
1025 mergeAndTestRewrite(
1026 Code: "aaa", Intermediate: "abbab", Result: "ccdd",
1027 First: toReplacements(Replaces: {{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}),
1028 Second: toReplacements(Replaces: {{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}));
1029 mergeAndTestRewrite(
1030 Code: "aa", Intermediate: "babbab", Result: "ccdd",
1031 First: toReplacements(Replaces: {{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}),
1032 Second: toReplacements(Replaces: {{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}));
1033}
1034
1035static constexpr bool usesWindowsPaths() {
1036 return is_style_windows(S: llvm::sys::path::Style::native);
1037}
1038
1039TEST(DeduplicateByFileTest, PathsWithDots) {
1040 std::map<std::string, Replacements> FileToReplaces;
1041 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS(
1042 new llvm::vfs::InMemoryFileSystem());
1043 FileManager FileMgr(FileSystemOptions(), VFS);
1044 StringRef Path1 = usesWindowsPaths() ? "a\\b\\..\\.\\c.h" : "a/b/.././c.h";
1045 StringRef Path2 = usesWindowsPaths() ? "a\\c.h" : "a/c.h";
1046 EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer("")));
1047 EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer("")));
1048 FileToReplaces[std::string(Path1)] = Replacements();
1049 FileToReplaces[std::string(Path2)] = Replacements();
1050 FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces);
1051 EXPECT_EQ(1u, FileToReplaces.size());
1052 EXPECT_EQ(Path1, FileToReplaces.begin()->first);
1053}
1054
1055TEST(DeduplicateByFileTest, PathWithDotSlash) {
1056 std::map<std::string, Replacements> FileToReplaces;
1057 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS(
1058 new llvm::vfs::InMemoryFileSystem());
1059 FileManager FileMgr(FileSystemOptions(), VFS);
1060 StringRef Path1 = usesWindowsPaths() ? ".\\a\\b\\c.h" : "./a/b/c.h";
1061 StringRef Path2 = usesWindowsPaths() ? "a\\b\\c.h" : "a/b/c.h";
1062 EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer("")));
1063 EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer("")));
1064 FileToReplaces[std::string(Path1)] = Replacements();
1065 FileToReplaces[std::string(Path2)] = Replacements();
1066 FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces);
1067 EXPECT_EQ(1u, FileToReplaces.size());
1068 EXPECT_EQ(Path1, FileToReplaces.begin()->first);
1069}
1070
1071TEST(DeduplicateByFileTest, NonExistingFilePath) {
1072 std::map<std::string, Replacements> FileToReplaces;
1073 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS(
1074 new llvm::vfs::InMemoryFileSystem());
1075 FileManager FileMgr(FileSystemOptions(), VFS);
1076 StringRef Path1 = usesWindowsPaths() ? ".\\a\\b\\c.h" : "./a/b/c.h";
1077 StringRef Path2 = usesWindowsPaths() ? "a\\b\\c.h" : "a/b/c.h";
1078 FileToReplaces[std::string(Path1)] = Replacements();
1079 FileToReplaces[std::string(Path2)] = Replacements();
1080 FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces);
1081 EXPECT_TRUE(FileToReplaces.empty());
1082}
1083
1084class AtomicChangeTest : public ::testing::Test {
1085 protected:
1086 void SetUp() override {
1087 DefaultFileID = Context.createInMemoryFile(Name: "input.cpp", Content: DefaultCode);
1088 DefaultLoc = Context.Sources.getLocForStartOfFile(FID: DefaultFileID)
1089 .getLocWithOffset(Offset: 20);
1090 assert(DefaultLoc.isValid() && "Default location must be valid.");
1091 }
1092
1093 RewriterTestContext Context;
1094 std::string DefaultCode = std::string(100, 'a');
1095 unsigned DefaultOffset = 20;
1096 SourceLocation DefaultLoc;
1097 FileID DefaultFileID;
1098};
1099
1100TEST_F(AtomicChangeTest, AtomicChangeToYAML) {
1101 AtomicChange Change(Context.Sources, DefaultLoc);
1102 llvm::Error Err =
1103 Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "aa", /*InsertAfter=*/false);
1104 ASSERT_TRUE(!Err);
1105 Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc.getLocWithOffset(Offset: 10), Text: "bb",
1106 /*InsertAfter=*/false);
1107 ASSERT_TRUE(!Err);
1108 Change.addHeader(Header: "a.h");
1109 Change.removeHeader(Header: "b.h");
1110 std::string YAMLString = Change.toYAMLString();
1111
1112 // NOTE: If this test starts to fail for no obvious reason, check whitespace.
1113 ASSERT_STREQ("---\n"
1114 "Key: 'input.cpp:20'\n"
1115 "FilePath: input.cpp\n"
1116 "Error: ''\n"
1117 "InsertedHeaders:\n"
1118 " - a.h\n"
1119 "RemovedHeaders:\n"
1120 " - b.h\n"
1121 "Replacements:\n"
1122 " - FilePath: input.cpp\n"
1123 " Offset: 20\n"
1124 " Length: 0\n"
1125 " ReplacementText: aa\n"
1126 " - FilePath: input.cpp\n"
1127 " Offset: 30\n"
1128 " Length: 0\n"
1129 " ReplacementText: bb\n"
1130 "...\n",
1131 YAMLString.c_str());
1132}
1133
1134TEST_F(AtomicChangeTest, YAMLToAtomicChange) {
1135 std::string YamlContent = "---\n"
1136 "Key: 'input.cpp:20'\n"
1137 "FilePath: input.cpp\n"
1138 "Error: 'ok'\n"
1139 "InsertedHeaders:\n"
1140 " - a.h\n"
1141 "RemovedHeaders:\n"
1142 " - b.h\n"
1143 "Replacements:\n"
1144 " - FilePath: input.cpp\n"
1145 " Offset: 20\n"
1146 " Length: 0\n"
1147 " ReplacementText: aa\n"
1148 " - FilePath: input.cpp\n"
1149 " Offset: 30\n"
1150 " Length: 0\n"
1151 " ReplacementText: bb\n"
1152 "...\n";
1153 AtomicChange ExpectedChange(Context.Sources, DefaultLoc);
1154 llvm::Error Err = ExpectedChange.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "aa",
1155 /*InsertAfter=*/false);
1156 ASSERT_TRUE(!Err);
1157 Err = ExpectedChange.insert(SM: Context.Sources, Loc: DefaultLoc.getLocWithOffset(Offset: 10),
1158 Text: "bb", /*InsertAfter=*/false);
1159 ASSERT_TRUE(!Err);
1160
1161 ExpectedChange.addHeader(Header: "a.h");
1162 ExpectedChange.removeHeader(Header: "b.h");
1163 ExpectedChange.setError("ok");
1164
1165 AtomicChange ActualChange = AtomicChange::convertFromYAML(YAMLContent: YamlContent);
1166 EXPECT_EQ(ExpectedChange.getKey(), ActualChange.getKey());
1167 EXPECT_EQ(ExpectedChange.getFilePath(), ActualChange.getFilePath());
1168 EXPECT_EQ(ExpectedChange.getError(), ActualChange.getError());
1169 EXPECT_EQ(ExpectedChange.getInsertedHeaders(),
1170 ActualChange.getInsertedHeaders());
1171 EXPECT_EQ(ExpectedChange.getRemovedHeaders(),
1172 ActualChange.getRemovedHeaders());
1173 EXPECT_EQ(ExpectedChange.getReplacements().size(),
1174 ActualChange.getReplacements().size());
1175 EXPECT_EQ(2u, ActualChange.getReplacements().size());
1176 EXPECT_EQ(*ExpectedChange.getReplacements().begin(),
1177 *ActualChange.getReplacements().begin());
1178 EXPECT_EQ(*(++ExpectedChange.getReplacements().begin()),
1179 *(++ActualChange.getReplacements().begin()));
1180}
1181
1182TEST_F(AtomicChangeTest, CheckKeyAndKeyFile) {
1183 AtomicChange Change(Context.Sources, DefaultLoc);
1184 EXPECT_EQ("input.cpp:20", Change.getKey());
1185 EXPECT_EQ("input.cpp", Change.getFilePath());
1186}
1187
1188TEST_F(AtomicChangeTest, Replace) {
1189 AtomicChange Change(Context.Sources, DefaultLoc);
1190 llvm::Error Err = Change.replace(SM: Context.Sources, Loc: DefaultLoc, Length: 2, Text: "aa");
1191 ASSERT_TRUE(!Err);
1192 EXPECT_EQ(Change.getReplacements().size(), 1u);
1193 EXPECT_EQ(*Change.getReplacements().begin(),
1194 Replacement(Context.Sources, DefaultLoc, 2, "aa"));
1195
1196 // Add a new replacement that conflicts with the existing one.
1197 Err = Change.replace(SM: Context.Sources, Loc: DefaultLoc, Length: 3, Text: "ab");
1198 EXPECT_TRUE((bool)Err);
1199 llvm::consumeError(Err: std::move(Err));
1200 EXPECT_EQ(Change.getReplacements().size(), 1u);
1201}
1202
1203TEST_F(AtomicChangeTest, ReplaceWithRange) {
1204 AtomicChange Change(Context.Sources, DefaultLoc);
1205 SourceLocation End = DefaultLoc.getLocWithOffset(Offset: 20);
1206 llvm::Error Err = Change.replace(
1207 SM: Context.Sources, Range: CharSourceRange::getCharRange(B: DefaultLoc, E: End), ReplacementText: "aa");
1208 ASSERT_TRUE(!Err);
1209 EXPECT_EQ(Change.getReplacements().size(), 1u);
1210 EXPECT_EQ(*Change.getReplacements().begin(),
1211 Replacement(Context.Sources, DefaultLoc, 20, "aa"));
1212}
1213
1214TEST_F(AtomicChangeTest, InsertBefore) {
1215 AtomicChange Change(Context.Sources, DefaultLoc);
1216 llvm::Error Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "aa");
1217 ASSERT_TRUE(!Err);
1218 EXPECT_EQ(Change.getReplacements().size(), 1u);
1219 EXPECT_EQ(*Change.getReplacements().begin(),
1220 Replacement(Context.Sources, DefaultLoc, 0, "aa"));
1221 Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "b", /*InsertAfter=*/false);
1222 ASSERT_TRUE(!Err);
1223 EXPECT_EQ(Change.getReplacements().size(), 1u);
1224 EXPECT_EQ(*Change.getReplacements().begin(),
1225 Replacement(Context.Sources, DefaultLoc, 0, "baa"));
1226}
1227
1228TEST_F(AtomicChangeTest, InsertAfter) {
1229 AtomicChange Change(Context.Sources, DefaultLoc);
1230 llvm::Error Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "aa");
1231 ASSERT_TRUE(!Err);
1232 EXPECT_EQ(Change.getReplacements().size(), 1u);
1233 EXPECT_EQ(*Change.getReplacements().begin(),
1234 Replacement(Context.Sources, DefaultLoc, 0, "aa"));
1235 Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "b");
1236 ASSERT_TRUE(!Err);
1237 EXPECT_EQ(Change.getReplacements().size(), 1u);
1238 EXPECT_EQ(*Change.getReplacements().begin(),
1239 Replacement(Context.Sources, DefaultLoc, 0, "aab"));
1240}
1241
1242TEST_F(AtomicChangeTest, InsertBeforeWithInvalidLocation) {
1243 AtomicChange Change(Context.Sources, DefaultLoc);
1244 llvm::Error Err =
1245 Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "a", /*InsertAfter=*/false);
1246 ASSERT_TRUE(!Err);
1247
1248 // Invalid location.
1249 Err = Change.insert(SM: Context.Sources, Loc: SourceLocation(), Text: "a",
1250 /*InsertAfter=*/false);
1251 ASSERT_TRUE((bool)Err);
1252 EXPECT_TRUE(checkReplacementError(
1253 std::move(Err), replacement_error::wrong_file_path,
1254 Replacement(Context.Sources, DefaultLoc, 0, "a"),
1255 Replacement(Context.Sources, SourceLocation(), 0, "a")));
1256}
1257
1258TEST_F(AtomicChangeTest, InsertBeforeToWrongFile) {
1259 AtomicChange Change(Context.Sources, DefaultLoc);
1260 llvm::Error Err =
1261 Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "a", /*InsertAfter=*/false);
1262 ASSERT_TRUE(!Err);
1263
1264 // Inserting at a different file.
1265 FileID NewID = Context.createInMemoryFile(Name: "extra.cpp", Content: DefaultCode);
1266 SourceLocation NewLoc = Context.Sources.getLocForStartOfFile(FID: NewID);
1267 Err = Change.insert(SM: Context.Sources, Loc: NewLoc, Text: "b", /*InsertAfter=*/false);
1268 ASSERT_TRUE((bool)Err);
1269 EXPECT_TRUE(
1270 checkReplacementError(std::move(Err), replacement_error::wrong_file_path,
1271 Replacement(Context.Sources, DefaultLoc, 0, "a"),
1272 Replacement(Context.Sources, NewLoc, 0, "b")));
1273}
1274
1275TEST_F(AtomicChangeTest, InsertAfterWithInvalidLocation) {
1276 AtomicChange Change(Context.Sources, DefaultLoc);
1277 llvm::Error Err = Change.insert(SM: Context.Sources, Loc: DefaultLoc, Text: "a");
1278 ASSERT_TRUE(!Err);
1279
1280 // Invalid location.
1281 Err = Change.insert(SM: Context.Sources, Loc: SourceLocation(), Text: "b");
1282 ASSERT_TRUE((bool)Err);
1283 EXPECT_TRUE(checkReplacementError(
1284 std::move(Err), replacement_error::wrong_file_path,
1285 Replacement(Context.Sources, DefaultLoc, 0, "a"),
1286 Replacement(Context.Sources, SourceLocation(), 0, "b")));
1287}
1288
1289TEST_F(AtomicChangeTest, Metadata) {
1290 AtomicChange Change(Context.Sources, DefaultLoc, 17);
1291 const llvm::Any &Metadata = Change.getMetadata();
1292 ASSERT_TRUE(llvm::any_cast<int>(&Metadata));
1293 EXPECT_EQ(llvm::any_cast<int>(Metadata), 17);
1294}
1295
1296TEST_F(AtomicChangeTest, NoMetadata) {
1297 AtomicChange Change(Context.Sources, DefaultLoc);
1298 EXPECT_FALSE(Change.getMetadata().has_value());
1299}
1300
1301class ApplyAtomicChangesTest : public ::testing::Test {
1302protected:
1303 ApplyAtomicChangesTest() : FilePath("file.cc") {
1304 Spec.Cleanup = true;
1305 Spec.Format = ApplyChangesSpec::kAll;
1306 Spec.Style = format::getLLVMStyle();
1307 }
1308
1309 ~ApplyAtomicChangesTest() override {}
1310
1311 void setInput(llvm::StringRef Input) {
1312 Code = std::string(Input);
1313 FID = Context.createInMemoryFile(Name: FilePath, Content: Code);
1314 }
1315
1316 SourceLocation getLoc(unsigned Offset) const {
1317 return Context.Sources.getLocForStartOfFile(FID).getLocWithOffset(Offset);
1318 }
1319
1320 AtomicChange replacementToAtomicChange(llvm::StringRef Key, unsigned Offset,
1321 unsigned Length,
1322 llvm::StringRef Text) {
1323 AtomicChange Change(FilePath, Key);
1324 llvm::Error Err =
1325 Change.replace(SM: Context.Sources, Loc: getLoc(Offset), Length, Text);
1326 EXPECT_FALSE(Err);
1327 return Change;
1328 }
1329
1330 std::string rewrite(bool FailureExpected = false) {
1331 llvm::Expected<std::string> ChangedCode =
1332 applyAtomicChanges(FilePath, Code, Changes, Spec);
1333 EXPECT_EQ(FailureExpected, !ChangedCode);
1334 if (!ChangedCode) {
1335 llvm::errs() << "Failed to apply changes: "
1336 << llvm::toString(E: ChangedCode.takeError()) << "\n";
1337 return "";
1338 }
1339 return *ChangedCode;
1340 }
1341
1342 RewriterTestContext Context;
1343 FileID FID;
1344 ApplyChangesSpec Spec;
1345 std::string Code;
1346 std::string FilePath;
1347 llvm::SmallVector<AtomicChange, 8> Changes;
1348};
1349
1350TEST_F(ApplyAtomicChangesTest, BasicRefactoring) {
1351 setInput("int a;");
1352 AtomicChange Change(FilePath, "key1");
1353 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 4, Length: 1, Text: "b"));
1354 EXPECT_EQ("int b;", rewrite());
1355}
1356
1357TEST_F(ApplyAtomicChangesTest, SeveralRefactorings) {
1358 setInput("int a;\n"
1359 "int b;");
1360 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 3, Text: "float"));
1361 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 4, Length: 1, Text: "f"));
1362 Changes.push_back(Elt: replacementToAtomicChange(Key: "key3", Offset: 11, Length: 1, Text: "g"));
1363 Changes.push_back(Elt: replacementToAtomicChange(Key: "key4", Offset: 7, Length: 3, Text: "float"));
1364 EXPECT_EQ("float f;\n"
1365 "float g;",
1366 rewrite());
1367}
1368
1369TEST_F(ApplyAtomicChangesTest, IgnorePathsInRefactorings) {
1370 setInput("int a;\n"
1371 "int b;");
1372 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 4, Length: 1, Text: "aa"));
1373
1374 FileID ID = Context.createInMemoryFile(Name: "AnotherFile", Content: "12345678912345");
1375 Changes.emplace_back(Args: "AnotherFile", Args: "key2");
1376 auto Err = Changes.back().replace(
1377 SM: Context.Sources,
1378 Loc: Context.Sources.getLocForStartOfFile(FID: ID).getLocWithOffset(Offset: 11), Length: 1, Text: "bb");
1379 ASSERT_TRUE(!Err);
1380 EXPECT_EQ("int aa;\n"
1381 "int bb;",
1382 rewrite());
1383}
1384
1385TEST_F(ApplyAtomicChangesTest, AppliesDuplicateInsertions) {
1386 setInput("int a;");
1387 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 5, Length: 0, Text: "b"));
1388 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 5, Length: 0, Text: "b"));
1389 EXPECT_EQ("int abb;", rewrite());
1390}
1391
1392TEST_F(ApplyAtomicChangesTest, BailsOnOverlappingRefactorings) {
1393 setInput("int a;");
1394 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 5, Text: "float f"));
1395 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 4, Length: 1, Text: "b"));
1396 EXPECT_EQ("", rewrite(/*FailureExpected=*/true));
1397}
1398
1399TEST_F(ApplyAtomicChangesTest, BasicReformatting) {
1400 setInput("int a;");
1401 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 5, Length: 1, Text: "b"));
1402 EXPECT_EQ("int b;", rewrite());
1403}
1404
1405TEST_F(ApplyAtomicChangesTest, OnlyFormatWhenViolateColumnLimits) {
1406 Spec.Format = ApplyChangesSpec::kViolations;
1407 Spec.Style.ColumnLimit = 8;
1408 setInput("int a;\n"
1409 "int a;\n"
1410 "int aaaaaaaa;\n");
1411 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 5, Length: 1, Text: "x"));
1412 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 15, Length: 1, Text: "x"));
1413 Changes.push_back(Elt: replacementToAtomicChange(Key: "key3", Offset: 23, Length: 8, Text: "xx"));
1414 EXPECT_EQ("int x;\n"
1415 "int x;\n"
1416 "int xx;\n",
1417 rewrite());
1418}
1419
1420TEST_F(ApplyAtomicChangesTest, LastLineViolateColumnLimits) {
1421 Spec.Format = ApplyChangesSpec::kViolations;
1422 Spec.Style.ColumnLimit = 8;
1423 setInput("int a;\n"
1424 "int a;");
1425 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 1, Text: "i"));
1426 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 15, Length: 2, Text: "y;"));
1427 EXPECT_EQ("int a;\n"
1428 "int y;",
1429 rewrite());
1430}
1431
1432TEST_F(ApplyAtomicChangesTest, LastLineWithNewlineViolateColumnLimits) {
1433 Spec.Format = ApplyChangesSpec::kViolations;
1434 Spec.Style.ColumnLimit = 8;
1435 setInput("int a;\n"
1436 "int a;\n");
1437 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 1, Text: "i"));
1438 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 14, Length: 3, Text: "y;\n"));
1439 EXPECT_EQ("int a;\n"
1440 "int y;\n",
1441 rewrite());
1442}
1443
1444TEST_F(ApplyAtomicChangesTest, Longer) {
1445 setInput("int a;");
1446 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 5, Length: 1, Text: "bbb"));
1447 EXPECT_EQ("int bbb;", rewrite());
1448}
1449
1450TEST_F(ApplyAtomicChangesTest, Shorter) {
1451 setInput("int aaa;");
1452 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 5, Length: 3, Text: "b"));
1453 EXPECT_EQ("int b;", rewrite());
1454}
1455
1456TEST_F(ApplyAtomicChangesTest, OnlyFormatChangedLines) {
1457 setInput("int aaa;\n"
1458 "int a = b;\n"
1459 "int bbb;");
1460 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 14, Length: 1, Text: "b"));
1461 EXPECT_EQ("int aaa;\n"
1462 "int b = b;\n"
1463 "int bbb;",
1464 rewrite());
1465}
1466
1467TEST_F(ApplyAtomicChangesTest, DisableFormatting) {
1468 Spec.Format = ApplyChangesSpec::kNone;
1469 setInput("int aaa;\n"
1470 "int a = b;\n"
1471 "int bbb;");
1472 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 14, Length: 1, Text: "b"));
1473 EXPECT_EQ("int aaa;\n"
1474 "int b = b;\n"
1475 "int bbb;",
1476 rewrite());
1477}
1478
1479TEST_F(ApplyAtomicChangesTest, AdaptsToLocalPointerStyle) {
1480 setInput("int *aaa;\n"
1481 "int *bbb;");
1482 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 0, Text: "int* ccc;\n"));
1483 EXPECT_EQ("int *ccc;\n"
1484 "int *aaa;\n"
1485 "int *bbb;",
1486 rewrite());
1487}
1488
1489TEST_F(ApplyAtomicChangesTest, AcceptsSurroundingFormatting) {
1490 setInput(" int aaa;\n"
1491 " int a = b;\n"
1492 " int bbb;");
1493 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 20, Length: 1, Text: "b"));
1494 EXPECT_EQ(" int aaa;\n"
1495 " int b = b;\n"
1496 " int bbb;",
1497 rewrite());
1498}
1499
1500TEST_F(ApplyAtomicChangesTest, BailsOutOnConflictingChanges) {
1501 setInput("int c;\n"
1502 "int f;");
1503 // Insertions at the same offset are only allowed in the same AtomicChange.
1504 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 0, Text: "int a;\n"));
1505 Changes.push_back(Elt: replacementToAtomicChange(Key: "key2", Offset: 0, Length: 0, Text: "int b;\n"));
1506 EXPECT_EQ("", rewrite(/*FailureExpected=*/true));
1507}
1508
1509TEST_F(ApplyAtomicChangesTest, InsertsNewIncludesInRightOrder) {
1510 setInput("int a;");
1511 Changes.emplace_back(Args&: FilePath, Args: "key1");
1512 Changes.back().addHeader(Header: "b");
1513 Changes.back().addHeader(Header: "c");
1514 Changes.emplace_back(Args&: FilePath, Args: "key2");
1515 Changes.back().addHeader(Header: "a");
1516 EXPECT_EQ("#include \"a\"\n"
1517 "#include \"b\"\n"
1518 "#include \"c\"\n"
1519 "int a;",
1520 rewrite());
1521}
1522
1523TEST_F(ApplyAtomicChangesTest, RemoveAndSortIncludes) {
1524 setInput("#include \"a\"\n"
1525 "#include \"b\"\n"
1526 "#include \"c\"\n"
1527 "\n"
1528 "int a;");
1529 Changes.emplace_back(Args&: FilePath, Args: "key1");
1530 Changes.back().removeHeader(Header: "b");
1531 EXPECT_EQ("#include \"a\"\n"
1532 "#include \"c\"\n"
1533 "\n"
1534 "int a;",
1535 rewrite());
1536}
1537TEST_F(ApplyAtomicChangesTest, InsertsSystemIncludes) {
1538 setInput("#include <asys>\n"
1539 "#include <csys>\n"
1540 "\n"
1541 "#include \"a\"\n"
1542 "#include \"c\"\n");
1543 Changes.emplace_back(Args&: FilePath, Args: "key1");
1544 Changes.back().addHeader(Header: "<asys>"); // Already exists.
1545 Changes.back().addHeader(Header: "<b>");
1546 Changes.back().addHeader(Header: "<d>");
1547 Changes.back().addHeader(Header: "\"b-already-escaped\"");
1548 EXPECT_EQ("#include <asys>\n"
1549 "#include <b>\n"
1550 "#include <csys>\n"
1551 "#include <d>\n"
1552 "\n"
1553 "#include \"a\"\n"
1554 "#include \"b-already-escaped\"\n"
1555 "#include \"c\"\n",
1556 rewrite());
1557}
1558
1559TEST_F(ApplyAtomicChangesTest, RemoveSystemIncludes) {
1560 setInput("#include <a>\n"
1561 "#include <b>\n"
1562 "\n"
1563 "#include \"c\""
1564 "\n"
1565 "int a;");
1566 Changes.emplace_back(Args&: FilePath, Args: "key1");
1567 Changes.back().removeHeader(Header: "<a>");
1568 EXPECT_EQ("#include <b>\n"
1569 "\n"
1570 "#include \"c\""
1571 "\n"
1572 "int a;",
1573 rewrite());
1574}
1575
1576TEST_F(ApplyAtomicChangesTest,
1577 DoNotFormatFollowingLinesIfSeparatedWithNewline) {
1578 setInput("#ifndef __H__\n"
1579 "#define __H__\n"
1580 "#include \"b\"\n"
1581 "\n"
1582 "int a;\n"
1583 "int a;\n"
1584 "int a;\n"
1585 "#endif // __H__\n");
1586 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1",
1587 Offset: llvm::StringRef("#ifndef __H__\n"
1588 "#define __H__\n"
1589 "\n"
1590 "#include \"b\"\n"
1591 "int a;\n"
1592 "int ")
1593 .size(),
1594 Length: 1, Text: "b"));
1595 Changes.back().addHeader(Header: "a");
1596 EXPECT_EQ("#ifndef __H__\n"
1597 "#define __H__\n"
1598 "#include \"a\"\n"
1599 "#include \"b\"\n"
1600 "\n"
1601 "int a;\n"
1602 "int b;\n"
1603 "int a;\n"
1604 "#endif // __H__\n",
1605 rewrite());
1606}
1607
1608TEST_F(ApplyAtomicChangesTest, FormatsCorrectLineWhenHeaderIsRemoved) {
1609 setInput("#include \"a\"\n"
1610 "\n"
1611 "int a;\n"
1612 "int a;\n"
1613 "int a;");
1614 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 27, Length: 1, Text: "b"));
1615 Changes.back().removeHeader(Header: "a");
1616 EXPECT_EQ("\n"
1617 "int a;\n"
1618 "int b;\n"
1619 "int a;",
1620 rewrite());
1621}
1622
1623TEST_F(ApplyAtomicChangesTest, CleansUpCtorInitializers) {
1624 setInput("A::A() : a(), b() {}\n"
1625 "A::A() : a(), b() {}\n"
1626 "A::A() : a(), b() {}\n"
1627 "A::A() : a()/**/, b() {}\n"
1628 "A::A() : a() ,// \n"
1629 " /**/ b() {}");
1630 Changes.emplace_back(Args&: FilePath, Args: "key1");
1631 auto Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 9), Length: 3, Text: "");
1632 ASSERT_TRUE(!Err);
1633 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 35), Length: 3, Text: "");
1634 ASSERT_TRUE(!Err);
1635 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 51), Length: 3, Text: "");
1636 ASSERT_TRUE(!Err);
1637 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 56), Length: 3, Text: "");
1638 ASSERT_TRUE(!Err);
1639 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 72), Length: 3, Text: "");
1640 ASSERT_TRUE(!Err);
1641 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 97), Length: 3, Text: "");
1642 ASSERT_TRUE(!Err);
1643 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 118), Length: 3, Text: "");
1644 ASSERT_TRUE(!Err);
1645 EXPECT_EQ("A::A() : b() {}\n"
1646 "A::A() : a() {}\n"
1647 "A::A() {}\n"
1648 "A::A() : b() {}\n"
1649 "A::A() {}",
1650 rewrite());
1651}
1652
1653TEST_F(ApplyAtomicChangesTest, CleansUpParameterLists) {
1654 setInput("void f(int i, float f, string s);\n"
1655 "f(1, 2.0f, \"a\");\n"
1656 "g(1, 1);");
1657 Changes.emplace_back(Args&: FilePath, Args: "key1");
1658 auto Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 7), Length: 5, Text: "");
1659 ASSERT_TRUE(!Err);
1660 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 23), Length: 8, Text: "");
1661 ASSERT_TRUE(!Err);
1662 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 36), Length: 1, Text: "");
1663 ASSERT_TRUE(!Err);
1664 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 45), Length: 3, Text: "");
1665 ASSERT_TRUE(!Err);
1666 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 53), Length: 1, Text: "");
1667 ASSERT_TRUE(!Err);
1668 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 56), Length: 1, Text: "");
1669 ASSERT_TRUE(!Err);
1670 EXPECT_EQ("void f(float f);\n"
1671 "f(2.0f);\n"
1672 "g();",
1673 rewrite());
1674}
1675
1676TEST_F(ApplyAtomicChangesTest, DisableCleanup) {
1677 Spec.Cleanup = false;
1678 setInput("void f(int i, float f, string s);\n"
1679 "f(1, 2.0f, \"a\");\n"
1680 "g(1, 1);");
1681 Changes.emplace_back(Args&: FilePath, Args: "key1");
1682 auto Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 7), Length: 5, Text: "");
1683 ASSERT_TRUE(!Err);
1684 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 23), Length: 8, Text: "");
1685 ASSERT_TRUE(!Err);
1686 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 36), Length: 1, Text: "");
1687 ASSERT_TRUE(!Err);
1688 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 45), Length: 3, Text: "");
1689 ASSERT_TRUE(!Err);
1690 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 53), Length: 1, Text: "");
1691 ASSERT_TRUE(!Err);
1692 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 56), Length: 1, Text: "");
1693 ASSERT_TRUE(!Err);
1694 EXPECT_EQ("void f(, float f, );\n"
1695 "f(, 2.0f, );\n"
1696 "g(, );",
1697 rewrite());
1698}
1699
1700TEST_F(ApplyAtomicChangesTest, EverythingDeleted) {
1701 setInput("int a;");
1702 Changes.push_back(Elt: replacementToAtomicChange(Key: "key1", Offset: 0, Length: 6, Text: ""));
1703 EXPECT_EQ("", rewrite());
1704}
1705
1706TEST_F(ApplyAtomicChangesTest, DoesNotDeleteInserts) {
1707 setInput("int a;\n"
1708 "int b;");
1709 Changes.emplace_back(Args&: FilePath, Args: "key1");
1710 auto Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 4), Length: 1, Text: "");
1711 ASSERT_TRUE(!Err);
1712 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 4), Length: 0, Text: "b");
1713 ASSERT_TRUE(!Err);
1714 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 11), Length: 0, Text: "a");
1715 ASSERT_TRUE(!Err);
1716 Err = Changes.back().replace(SM: Context.Sources, Loc: getLoc(Offset: 11), Length: 1, Text: "");
1717 ASSERT_TRUE(!Err);
1718 EXPECT_EQ("int b;\n"
1719 "int a;",
1720 rewrite());
1721}
1722
1723} // end namespace tooling
1724} // end namespace clang
1725

source code of clang/unittests/Tooling/RefactoringTest.cpp