1 //===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===// 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 "RewriterTestContext.h" 11 #include "clang/AST/ASTConsumer.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/AST/DeclGroup.h" 15 #include "clang/AST/RecursiveASTVisitor.h" 16 #include "clang/Basic/Diagnostic.h" 17 #include "clang/Basic/DiagnosticOptions.h" 18 #include "clang/Basic/FileManager.h" 19 #include "clang/Basic/LangOptions.h" 20 #include "clang/Basic/SourceManager.h" 21 #include "clang/Format/Format.h" 22 #include "clang/Frontend/CompilerInstance.h" 23 #include "clang/Frontend/FrontendAction.h" 24 #include "clang/Frontend/TextDiagnosticPrinter.h" 25 #include "clang/Rewrite/Core/Rewriter.h" 26 #include "clang/Tooling/Refactoring.h" 27 #include "clang/Tooling/Tooling.h" 28 #include "llvm/ADT/SmallString.h" 29 #include "llvm/Support/Path.h" 30 #include "gtest/gtest.h" 31 32 namespace clang { 33 namespace tooling { 34 35 class ReplacementTest : public ::testing::Test { 36 protected: 37 Replacement createReplacement(SourceLocation Start, unsigned Length, 38 llvm::StringRef ReplacementText) { 39 return Replacement(Context.Sources, Start, Length, ReplacementText); 40 } 41 42 RewriterTestContext Context; 43 }; 44 45 TEST_F(ReplacementTest, CanDeleteAllText) { 46 FileID ID = Context.createInMemoryFile("input.cpp", "text"); 47 SourceLocation Location = Context.getLocation(ID, 1, 1); 48 Replacement Replace(createReplacement(Location, 4, "")); 49 EXPECT_TRUE(Replace.apply(Context.Rewrite)); 50 EXPECT_EQ("", Context.getRewrittenText(ID)); 51 } 52 53 TEST_F(ReplacementTest, CanDeleteAllTextInTextWithNewlines) { 54 FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3"); 55 SourceLocation Location = Context.getLocation(ID, 1, 1); 56 Replacement Replace(createReplacement(Location, 17, "")); 57 EXPECT_TRUE(Replace.apply(Context.Rewrite)); 58 EXPECT_EQ("", Context.getRewrittenText(ID)); 59 } 60 61 TEST_F(ReplacementTest, CanAddText) { 62 FileID ID = Context.createInMemoryFile("input.cpp", ""); 63 SourceLocation Location = Context.getLocation(ID, 1, 1); 64 Replacement Replace(createReplacement(Location, 0, "result")); 65 EXPECT_TRUE(Replace.apply(Context.Rewrite)); 66 EXPECT_EQ("result", Context.getRewrittenText(ID)); 67 } 68 69 TEST_F(ReplacementTest, CanReplaceTextAtPosition) { 70 FileID ID = Context.createInMemoryFile("input.cpp", 71 "line1\nline2\nline3\nline4"); 72 SourceLocation Location = Context.getLocation(ID, 2, 3); 73 Replacement Replace(createReplacement(Location, 12, "x")); 74 EXPECT_TRUE(Replace.apply(Context.Rewrite)); 75 EXPECT_EQ("line1\nlixne4", Context.getRewrittenText(ID)); 76 } 77 78 TEST_F(ReplacementTest, CanReplaceTextAtPositionMultipleTimes) { 79 FileID ID = Context.createInMemoryFile("input.cpp", 80 "line1\nline2\nline3\nline4"); 81 SourceLocation Location1 = Context.getLocation(ID, 2, 3); 82 Replacement Replace1(createReplacement(Location1, 12, "x\ny\n")); 83 EXPECT_TRUE(Replace1.apply(Context.Rewrite)); 84 EXPECT_EQ("line1\nlix\ny\nne4", Context.getRewrittenText(ID)); 85 86 // Since the original source has not been modified, the (4, 4) points to the 87 // 'e' in the original content. 88 SourceLocation Location2 = Context.getLocation(ID, 4, 4); 89 Replacement Replace2(createReplacement(Location2, 1, "f")); 90 EXPECT_TRUE(Replace2.apply(Context.Rewrite)); 91 EXPECT_EQ("line1\nlix\ny\nnf4", Context.getRewrittenText(ID)); 92 } 93 94 TEST_F(ReplacementTest, ApplyFailsForNonExistentLocation) { 95 Replacement Replace("nonexistent-file.cpp", 0, 1, ""); 96 EXPECT_FALSE(Replace.apply(Context.Rewrite)); 97 } 98 99 TEST_F(ReplacementTest, CanRetrivePath) { 100 Replacement Replace("/path/to/file.cpp", 0, 1, ""); 101 EXPECT_EQ("/path/to/file.cpp", Replace.getFilePath()); 102 } 103 104 TEST_F(ReplacementTest, ReturnsInvalidPath) { 105 Replacement Replace1(Context.Sources, SourceLocation(), 0, ""); 106 EXPECT_TRUE(Replace1.getFilePath().empty()); 107 108 Replacement Replace2; 109 EXPECT_TRUE(Replace2.getFilePath().empty()); 110 } 111 112 TEST_F(ReplacementTest, CanApplyReplacements) { 113 FileID ID = Context.createInMemoryFile("input.cpp", 114 "line1\nline2\nline3\nline4"); 115 Replacements Replaces; 116 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 117 5, "replaced")); 118 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1), 119 5, "other")); 120 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); 121 EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID)); 122 } 123 124 // FIXME: Remove this test case when Replacements is implemented as std::vector 125 // instead of std::set. The other ReplacementTest tests will need to be updated 126 // at that point as well. 127 TEST_F(ReplacementTest, VectorCanApplyReplacements) { 128 FileID ID = Context.createInMemoryFile("input.cpp", 129 "line1\nline2\nline3\nline4"); 130 std::vector<Replacement> Replaces; 131 Replaces.push_back(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 132 5, "replaced")); 133 Replaces.push_back( 134 Replacement(Context.Sources, Context.getLocation(ID, 3, 1), 5, "other")); 135 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); 136 EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID)); 137 } 138 139 TEST_F(ReplacementTest, SkipsDuplicateReplacements) { 140 FileID ID = Context.createInMemoryFile("input.cpp", 141 "line1\nline2\nline3\nline4"); 142 Replacements Replaces; 143 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 144 5, "replaced")); 145 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 146 5, "replaced")); 147 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 148 5, "replaced")); 149 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); 150 EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID)); 151 } 152 153 TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) { 154 // This test depends on the value of the file name of an invalid source 155 // location being in the range ]a, z[. 156 FileID IDa = Context.createInMemoryFile("a.cpp", "text"); 157 FileID IDz = Context.createInMemoryFile("z.cpp", "text"); 158 Replacements Replaces; 159 Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1), 160 4, "a")); 161 Replaces.insert(Replacement(Context.Sources, SourceLocation(), 162 5, "2")); 163 Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1), 164 4, "z")); 165 EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite)); 166 EXPECT_EQ("a", Context.getRewrittenText(IDa)); 167 EXPECT_EQ("z", Context.getRewrittenText(IDz)); 168 } 169 170 TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) { 171 // Column limit is 20. 172 std::string Code1 = "Long *a =\n" 173 " new Long();\n" 174 "long x = 1;"; 175 std::string Expected1 = "auto a = new Long();\n" 176 "long x =\n" 177 " 12345678901;"; 178 std::string Code2 = "int x = 123;\n" 179 "int y = 0;"; 180 std::string Expected2 = "int x =\n" 181 " 1234567890123;\n" 182 "int y = 10;"; 183 FileID ID1 = Context.createInMemoryFile("format_1.cpp", Code1); 184 FileID ID2 = Context.createInMemoryFile("format_2.cpp", Code2); 185 186 tooling::Replacements Replaces; 187 // Scrambled the order of replacements. 188 Replaces.insert(tooling::Replacement( 189 Context.Sources, Context.getLocation(ID2, 1, 12), 0, "4567890123")); 190 Replaces.insert(tooling::Replacement( 191 Context.Sources, Context.getLocation(ID1, 1, 1), 6, "auto ")); 192 Replaces.insert(tooling::Replacement( 193 Context.Sources, Context.getLocation(ID2, 2, 9), 1, "10")); 194 Replaces.insert(tooling::Replacement( 195 Context.Sources, Context.getLocation(ID1, 3, 10), 1, "12345678901")); 196 197 EXPECT_TRUE(formatAndApplyAllReplacements( 198 Replaces, Context.Rewrite, "{BasedOnStyle: LLVM, ColumnLimit: 20}")); 199 EXPECT_EQ(Expected1, Context.getRewrittenText(ID1)); 200 EXPECT_EQ(Expected2, Context.getRewrittenText(ID2)); 201 } 202 203 TEST(ShiftedCodePositionTest, FindsNewCodePosition) { 204 Replacements Replaces; 205 Replaces.insert(Replacement("", 0, 1, "")); 206 Replaces.insert(Replacement("", 4, 3, " ")); 207 // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'. 208 EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i; 209 EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i; 210 EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i; 211 EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i; 212 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i; 213 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); // int | i; 214 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i; 215 EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |; 216 EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i| 217 } 218 219 // FIXME: Remove this test case when Replacements is implemented as std::vector 220 // instead of std::set. The other ReplacementTest tests will need to be updated 221 // at that point as well. 222 TEST(ShiftedCodePositionTest, VectorFindsNewCodePositionWithInserts) { 223 std::vector<Replacement> Replaces; 224 Replaces.push_back(Replacement("", 0, 1, "")); 225 Replaces.push_back(Replacement("", 4, 3, " ")); 226 // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'. 227 EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i; 228 EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i; 229 EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i; 230 EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i; 231 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i; 232 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 5)); // int | i; 233 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i; 234 EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |; 235 EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i| 236 } 237 238 TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) { 239 Replacements Replaces; 240 Replaces.insert(Replacement("", 4, 0, "\"\n\"")); 241 // Assume '"12345678"' is turned into '"1234"\n"5678"'. 242 EXPECT_EQ(3u, shiftedCodePosition(Replaces, 3)); // "123|5678" 243 EXPECT_EQ(7u, shiftedCodePosition(Replaces, 4)); // "1234|678" 244 EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "12345|78" 245 } 246 247 TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) { 248 Replacements Replaces; 249 // Replace the first four characters with "abcd". 250 Replaces.insert(Replacement("", 0, 4, "abcd")); 251 for (unsigned i = 0; i < 3; ++i) 252 EXPECT_EQ(i, shiftedCodePosition(Replaces, i)); 253 } 254 255 class FlushRewrittenFilesTest : public ::testing::Test { 256 public: 257 FlushRewrittenFilesTest() {} 258 259 ~FlushRewrittenFilesTest() override { 260 for (llvm::StringMap<std::string>::iterator I = TemporaryFiles.begin(), 261 E = TemporaryFiles.end(); 262 I != E; ++I) { 263 llvm::StringRef Name = I->second; 264 std::error_code EC = llvm::sys::fs::remove(Name); 265 (void)EC; 266 assert(!EC); 267 } 268 } 269 270 FileID createFile(llvm::StringRef Name, llvm::StringRef Content) { 271 SmallString<1024> Path; 272 int FD; 273 std::error_code EC = llvm::sys::fs::createTemporaryFile(Name, "", FD, Path); 274 assert(!EC); 275 (void)EC; 276 277 llvm::raw_fd_ostream OutStream(FD, true); 278 OutStream << Content; 279 OutStream.close(); 280 const FileEntry *File = Context.Files.getFile(Path); 281 assert(File != nullptr); 282 283 StringRef Found = 284 TemporaryFiles.insert(std::make_pair(Name, Path.str())).first->second; 285 assert(Found == Path); 286 (void)Found; 287 return Context.Sources.createFileID(File, SourceLocation(), SrcMgr::C_User); 288 } 289 290 std::string getFileContentFromDisk(llvm::StringRef Name) { 291 std::string Path = TemporaryFiles.lookup(Name); 292 assert(!Path.empty()); 293 // We need to read directly from the FileManager without relaying through 294 // a FileEntry, as otherwise we'd read through an already opened file 295 // descriptor, which might not see the changes made. 296 // FIXME: Figure out whether there is a way to get the SourceManger to 297 // reopen the file. 298 auto FileBuffer = Context.Files.getBufferForFile(Path); 299 return (*FileBuffer)->getBuffer(); 300 } 301 302 llvm::StringMap<std::string> TemporaryFiles; 303 RewriterTestContext Context; 304 }; 305 306 TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) { 307 FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4"); 308 Replacements Replaces; 309 Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 310 5, "replaced")); 311 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); 312 EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles()); 313 EXPECT_EQ("line1\nreplaced\nline3\nline4", 314 getFileContentFromDisk("input.cpp")); 315 } 316 317 namespace { 318 template <typename T> 319 class TestVisitor : public clang::RecursiveASTVisitor<T> { 320 public: 321 bool runOver(StringRef Code) { 322 return runToolOnCode(new TestAction(this), Code); 323 } 324 325 protected: 326 clang::SourceManager *SM; 327 clang::ASTContext *Context; 328 329 private: 330 class FindConsumer : public clang::ASTConsumer { 331 public: 332 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {} 333 334 void HandleTranslationUnit(clang::ASTContext &Context) override { 335 Visitor->TraverseDecl(Context.getTranslationUnitDecl()); 336 } 337 338 private: 339 TestVisitor *Visitor; 340 }; 341 342 class TestAction : public clang::ASTFrontendAction { 343 public: 344 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {} 345 346 std::unique_ptr<clang::ASTConsumer> 347 CreateASTConsumer(clang::CompilerInstance &compiler, 348 llvm::StringRef dummy) override { 349 Visitor->SM = &compiler.getSourceManager(); 350 Visitor->Context = &compiler.getASTContext(); 351 /// TestConsumer will be deleted by the framework calling us. 352 return llvm::make_unique<FindConsumer>(Visitor); 353 } 354 355 private: 356 TestVisitor *Visitor; 357 }; 358 }; 359 } // end namespace 360 361 void expectReplacementAt(const Replacement &Replace, 362 StringRef File, unsigned Offset, unsigned Length) { 363 ASSERT_TRUE(Replace.isApplicable()); 364 EXPECT_EQ(File, Replace.getFilePath()); 365 EXPECT_EQ(Offset, Replace.getOffset()); 366 EXPECT_EQ(Length, Replace.getLength()); 367 } 368 369 class ClassDeclXVisitor : public TestVisitor<ClassDeclXVisitor> { 370 public: 371 bool VisitCXXRecordDecl(CXXRecordDecl *Record) { 372 if (Record->getName() == "X") { 373 Replace = Replacement(*SM, Record, ""); 374 } 375 return true; 376 } 377 Replacement Replace; 378 }; 379 380 TEST(Replacement, CanBeConstructedFromNode) { 381 ClassDeclXVisitor ClassDeclX; 382 EXPECT_TRUE(ClassDeclX.runOver(" class X;")); 383 expectReplacementAt(ClassDeclX.Replace, "input.cc", 5, 7); 384 } 385 386 TEST(Replacement, ReplacesAtSpellingLocation) { 387 ClassDeclXVisitor ClassDeclX; 388 EXPECT_TRUE(ClassDeclX.runOver("#define A(Y) Y\nA(class X);")); 389 expectReplacementAt(ClassDeclX.Replace, "input.cc", 17, 7); 390 } 391 392 class CallToFVisitor : public TestVisitor<CallToFVisitor> { 393 public: 394 bool VisitCallExpr(CallExpr *Call) { 395 if (Call->getDirectCallee()->getName() == "F") { 396 Replace = Replacement(*SM, Call, ""); 397 } 398 return true; 399 } 400 Replacement Replace; 401 }; 402 403 TEST(Replacement, FunctionCall) { 404 CallToFVisitor CallToF; 405 EXPECT_TRUE(CallToF.runOver("void F(); void G() { F(); }")); 406 expectReplacementAt(CallToF.Replace, "input.cc", 21, 3); 407 } 408 409 TEST(Replacement, TemplatedFunctionCall) { 410 CallToFVisitor CallToF; 411 EXPECT_TRUE(CallToF.runOver( 412 "template <typename T> void F(); void G() { F<int>(); }")); 413 expectReplacementAt(CallToF.Replace, "input.cc", 43, 8); 414 } 415 416 class NestedNameSpecifierAVisitor 417 : public TestVisitor<NestedNameSpecifierAVisitor> { 418 public: 419 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLoc) { 420 if (NNSLoc.getNestedNameSpecifier()) { 421 if (const NamespaceDecl* NS = NNSLoc.getNestedNameSpecifier()->getAsNamespace()) { 422 if (NS->getName() == "a") { 423 Replace = Replacement(*SM, &NNSLoc, "", Context->getLangOpts()); 424 } 425 } 426 } 427 return TestVisitor<NestedNameSpecifierAVisitor>::TraverseNestedNameSpecifierLoc( 428 NNSLoc); 429 } 430 Replacement Replace; 431 }; 432 433 TEST(Replacement, ColonColon) { 434 NestedNameSpecifierAVisitor VisitNNSA; 435 EXPECT_TRUE(VisitNNSA.runOver("namespace a { void f() { ::a::f(); } }")); 436 expectReplacementAt(VisitNNSA.Replace, "input.cc", 25, 5); 437 } 438 439 TEST(Range, overlaps) { 440 EXPECT_TRUE(Range(10, 10).overlapsWith(Range(0, 11))); 441 EXPECT_TRUE(Range(0, 11).overlapsWith(Range(10, 10))); 442 EXPECT_FALSE(Range(10, 10).overlapsWith(Range(0, 10))); 443 EXPECT_FALSE(Range(0, 10).overlapsWith(Range(10, 10))); 444 EXPECT_TRUE(Range(0, 10).overlapsWith(Range(2, 6))); 445 EXPECT_TRUE(Range(2, 6).overlapsWith(Range(0, 10))); 446 } 447 448 TEST(Range, contains) { 449 EXPECT_TRUE(Range(0, 10).contains(Range(0, 10))); 450 EXPECT_TRUE(Range(0, 10).contains(Range(2, 6))); 451 EXPECT_FALSE(Range(2, 6).contains(Range(0, 10))); 452 EXPECT_FALSE(Range(0, 10).contains(Range(0, 11))); 453 } 454 455 TEST(Range, CalculateRangesOfReplacements) { 456 // Before: aaaabbbbbbz 457 // After : bbbbbbzzzzzzoooooooooooooooo 458 Replacements Replaces; 459 Replaces.insert(Replacement("foo", 0, 4, "")); 460 Replaces.insert(Replacement("foo", 10, 1, "zzzzzz")); 461 Replaces.insert(Replacement("foo", 11, 0, "oooooooooooooooo")); 462 463 std::vector<Range> Ranges = calculateChangedRanges(Replaces); 464 465 EXPECT_EQ(2ul, Ranges.size()); 466 EXPECT_TRUE(Ranges[0].getOffset() == 0); 467 EXPECT_TRUE(Ranges[0].getLength() == 0); 468 EXPECT_TRUE(Ranges[1].getOffset() == 6); 469 EXPECT_TRUE(Ranges[1].getLength() == 22); 470 } 471 472 TEST(Range, RangesAfterReplacements) { 473 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)}; 474 Replacements Replaces = {Replacement("foo", 0, 2, "1234")}; 475 std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)}; 476 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 477 } 478 479 TEST(Range, RangesBeforeReplacements) { 480 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)}; 481 Replacements Replaces = {Replacement("foo", 20, 2, "1234")}; 482 std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)}; 483 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 484 } 485 486 TEST(Range, NotAffectedByReplacements) { 487 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)}; 488 Replacements Replaces = {Replacement("foo", 3, 2, "12"), 489 Replacement("foo", 12, 2, "12"), 490 Replacement("foo", 20, 5, "")}; 491 std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5), 492 Range(20, 0)}; 493 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 494 } 495 496 TEST(Range, RangesWithNonOverlappingReplacements) { 497 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)}; 498 Replacements Replaces = {Replacement("foo", 3, 1, ""), 499 Replacement("foo", 6, 1, "123"), 500 Replacement("foo", 20, 2, "12345")}; 501 std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4), 502 Range(11, 5), Range(21, 5)}; 503 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 504 } 505 506 TEST(Range, RangesWithOverlappingReplacements) { 507 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5), 508 Range(30, 5)}; 509 Replacements Replaces = { 510 Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"), 511 Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")}; 512 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5), 513 Range(22, 0)}; 514 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 515 } 516 517 TEST(Range, MergeIntoOneRange) { 518 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)}; 519 Replacements Replaces = {Replacement("foo", 1, 15, "1234567890")}; 520 std::vector<Range> Expected = {Range(0, 15)}; 521 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 522 } 523 524 TEST(Range, ReplacementsStartingAtRangeOffsets) { 525 std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)}; 526 Replacements Replaces = { 527 Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"), 528 Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")}; 529 std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)}; 530 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 531 } 532 533 TEST(Range, ReplacementsEndingAtRangeEnds) { 534 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)}; 535 Replacements Replaces = {Replacement("foo", 6, 1, "123"), 536 Replacement("foo", 17, 3, "12")}; 537 std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)}; 538 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 539 } 540 541 TEST(Range, AjacentReplacements) { 542 std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)}; 543 Replacements Replaces = {Replacement("foo", 1, 2, "123"), 544 Replacement("foo", 12, 3, "1234")}; 545 std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)}; 546 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 547 } 548 549 TEST(Range, MergeRangesAfterReplacements) { 550 std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)}; 551 Replacements Replaces = {Replacement("foo", 1, 3, ""), 552 Replacement("foo", 7, 0, "12"), Replacement("foo", 9, 2, "")}; 553 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0), Range(8, 0)}; 554 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); 555 } 556 557 TEST(DeduplicateTest, removesDuplicates) { 558 std::vector<Replacement> Input; 559 Input.push_back(Replacement("fileA", 50, 0, " foo ")); 560 Input.push_back(Replacement("fileA", 10, 3, " bar ")); 561 Input.push_back(Replacement("fileA", 10, 2, " bar ")); // Length differs 562 Input.push_back(Replacement("fileA", 9, 3, " bar ")); // Offset differs 563 Input.push_back(Replacement("fileA", 50, 0, " foo ")); // Duplicate 564 Input.push_back(Replacement("fileA", 51, 3, " bar ")); 565 Input.push_back(Replacement("fileB", 51, 3, " bar ")); // Filename differs! 566 Input.push_back(Replacement("fileB", 60, 1, " bar ")); 567 Input.push_back(Replacement("fileA", 60, 2, " bar ")); 568 Input.push_back(Replacement("fileA", 51, 3, " moo ")); // Replacement text 569 // differs! 570 571 std::vector<Replacement> Expected; 572 Expected.push_back(Replacement("fileA", 9, 3, " bar ")); 573 Expected.push_back(Replacement("fileA", 10, 2, " bar ")); 574 Expected.push_back(Replacement("fileA", 10, 3, " bar ")); 575 Expected.push_back(Replacement("fileA", 50, 0, " foo ")); 576 Expected.push_back(Replacement("fileA", 51, 3, " bar ")); 577 Expected.push_back(Replacement("fileA", 51, 3, " moo ")); 578 Expected.push_back(Replacement("fileB", 60, 1, " bar ")); 579 Expected.push_back(Replacement("fileA", 60, 2, " bar ")); 580 581 std::vector<Range> Conflicts; // Ignored for this test 582 deduplicate(Input, Conflicts); 583 584 EXPECT_EQ(3U, Conflicts.size()); 585 EXPECT_EQ(Expected, Input); 586 } 587 588 TEST(DeduplicateTest, detectsConflicts) { 589 { 590 std::vector<Replacement> Input; 591 Input.push_back(Replacement("fileA", 0, 5, " foo ")); 592 Input.push_back(Replacement("fileA", 0, 5, " foo ")); // Duplicate not a 593 // conflict. 594 Input.push_back(Replacement("fileA", 2, 6, " bar ")); 595 Input.push_back(Replacement("fileA", 7, 3, " moo ")); 596 597 std::vector<Range> Conflicts; 598 deduplicate(Input, Conflicts); 599 600 // One duplicate is removed and the remaining three items form one 601 // conflicted range. 602 ASSERT_EQ(3u, Input.size()); 603 ASSERT_EQ(1u, Conflicts.size()); 604 ASSERT_EQ(0u, Conflicts.front().getOffset()); 605 ASSERT_EQ(3u, Conflicts.front().getLength()); 606 } 607 { 608 std::vector<Replacement> Input; 609 610 // Expected sorted order is shown. It is the sorted order to which the 611 // returned conflict info refers to. 612 Input.push_back(Replacement("fileA", 0, 5, " foo ")); // 0 613 Input.push_back(Replacement("fileA", 5, 5, " bar ")); // 1 614 Input.push_back(Replacement("fileA", 6, 0, " bar ")); // 3 615 Input.push_back(Replacement("fileA", 5, 5, " moo ")); // 2 616 Input.push_back(Replacement("fileA", 7, 2, " bar ")); // 4 617 Input.push_back(Replacement("fileA", 15, 5, " golf ")); // 5 618 Input.push_back(Replacement("fileA", 16, 5, " bag ")); // 6 619 Input.push_back(Replacement("fileA", 10, 3, " club ")); // 7 620 621 // #3 is special in that it is completely contained by another conflicting 622 // Replacement. #4 ensures #3 hasn't messed up the conflicting range size. 623 624 std::vector<Range> Conflicts; 625 deduplicate(Input, Conflicts); 626 627 // No duplicates 628 ASSERT_EQ(8u, Input.size()); 629 ASSERT_EQ(2u, Conflicts.size()); 630 ASSERT_EQ(1u, Conflicts[0].getOffset()); 631 ASSERT_EQ(4u, Conflicts[0].getLength()); 632 ASSERT_EQ(6u, Conflicts[1].getOffset()); 633 ASSERT_EQ(2u, Conflicts[1].getLength()); 634 } 635 } 636 637 class MergeReplacementsTest : public ::testing::Test { 638 protected: 639 void mergeAndTestRewrite(StringRef Code, StringRef Intermediate, 640 StringRef Result, const Replacements &First, 641 const Replacements &Second) { 642 // These are mainly to verify the test itself and make it easier to read. 643 auto AfterFirst = applyAllReplacements(Code, First); 644 EXPECT_TRUE(static_cast<bool>(AfterFirst)); 645 auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second); 646 EXPECT_TRUE(static_cast<bool>(InSequenceRewrite)); 647 EXPECT_EQ(Intermediate, *AfterFirst); 648 EXPECT_EQ(Result, *InSequenceRewrite); 649 650 tooling::Replacements Merged = mergeReplacements(First, Second); 651 auto MergedRewrite = applyAllReplacements(Code, Merged); 652 EXPECT_TRUE(static_cast<bool>(MergedRewrite)); 653 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite); 654 if (*InSequenceRewrite != *MergedRewrite) 655 for (tooling::Replacement M : Merged) 656 llvm::errs() << M.getOffset() << " " << M.getLength() << " " 657 << M.getReplacementText() << "\n"; 658 } 659 void mergeAndTestRewrite(StringRef Code, const Replacements &First, 660 const Replacements &Second) { 661 auto AfterFirst = applyAllReplacements(Code, First); 662 EXPECT_TRUE(static_cast<bool>(AfterFirst)); 663 auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second); 664 tooling::Replacements Merged = mergeReplacements(First, Second); 665 auto MergedRewrite = applyAllReplacements(Code, Merged); 666 EXPECT_TRUE(static_cast<bool>(MergedRewrite)); 667 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite); 668 if (*InSequenceRewrite != *MergedRewrite) 669 for (tooling::Replacement M : Merged) 670 llvm::errs() << M.getOffset() << " " << M.getLength() << " " 671 << M.getReplacementText() << "\n"; 672 } 673 }; 674 675 TEST_F(MergeReplacementsTest, Offsets) { 676 mergeAndTestRewrite("aaa", "aabab", "cacabab", 677 {{"", 2, 0, "b"}, {"", 3, 0, "b"}}, 678 {{"", 0, 0, "c"}, {"", 1, 0, "c"}}); 679 mergeAndTestRewrite("aaa", "babaa", "babacac", 680 {{"", 0, 0, "b"}, {"", 1, 0, "b"}}, 681 {{"", 4, 0, "c"}, {"", 5, 0, "c"}}); 682 mergeAndTestRewrite("aaaa", "aaa", "aac", {{"", 1, 1, ""}}, 683 {{"", 2, 1, "c"}}); 684 685 mergeAndTestRewrite("aa", "bbabba", "bbabcba", 686 {{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}, {{"", 4, 0, "c"}}); 687 } 688 689 TEST_F(MergeReplacementsTest, Concatenations) { 690 // Basic concatenations. It is important to merge these into a single 691 // replacement to ensure the correct order. 692 EXPECT_EQ((Replacements{{"", 0, 0, "ab"}}), 693 mergeReplacements({{"", 0, 0, "a"}}, {{"", 1, 0, "b"}})); 694 EXPECT_EQ((Replacements{{"", 0, 0, "ba"}}), 695 mergeReplacements({{"", 0, 0, "a"}}, {{"", 0, 0, "b"}})); 696 mergeAndTestRewrite("", "a", "ab", {{"", 0, 0, "a"}}, {{"", 1, 0, "b"}}); 697 mergeAndTestRewrite("", "a", "ba", {{"", 0, 0, "a"}}, {{"", 0, 0, "b"}}); 698 } 699 700 TEST_F(MergeReplacementsTest, NotChangingLengths) { 701 mergeAndTestRewrite("aaaa", "abba", "acca", {{"", 1, 2, "bb"}}, 702 {{"", 1, 2, "cc"}}); 703 mergeAndTestRewrite("aaaa", "abba", "abcc", {{"", 1, 2, "bb"}}, 704 {{"", 2, 2, "cc"}}); 705 mergeAndTestRewrite("aaaa", "abba", "ccba", {{"", 1, 2, "bb"}}, 706 {{"", 0, 2, "cc"}}); 707 mergeAndTestRewrite("aaaaaa", "abbdda", "abccda", 708 {{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}, {{"", 2, 2, "cc"}}); 709 } 710 711 TEST_F(MergeReplacementsTest, OverlappingRanges) { 712 mergeAndTestRewrite("aaa", "bbd", "bcbcd", 713 {{"", 0, 1, "bb"}, {"", 1, 2, "d"}}, 714 {{"", 1, 0, "c"}, {"", 2, 0, "c"}}); 715 716 mergeAndTestRewrite("aaaa", "aabbaa", "acccca", {{"", 2, 0, "bb"}}, 717 {{"", 1, 4, "cccc"}}); 718 mergeAndTestRewrite("aaaa", "aababa", "acccca", 719 {{"", 2, 0, "b"}, {"", 3, 0, "b"}}, {{"", 1, 4, "cccc"}}); 720 mergeAndTestRewrite("aaaaaa", "abbbba", "abba", {{"", 1, 4, "bbbb"}}, 721 {{"", 2, 2, ""}}); 722 mergeAndTestRewrite("aaaa", "aa", "cc", {{"", 1, 1, ""}, {"", 2, 1, ""}}, 723 {{"", 0, 2, "cc"}}); 724 mergeAndTestRewrite("aa", "abbba", "abcbcba", {{"", 1, 0, "bbb"}}, 725 {{"", 2, 0, "c"}, {"", 3, 0, "c"}}); 726 727 mergeAndTestRewrite("aaa", "abbab", "ccdd", 728 {{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}, 729 {{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}); 730 mergeAndTestRewrite("aa", "babbab", "ccdd", 731 {{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}, 732 {{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}); 733 } 734 735 } // end namespace tooling 736 } // end namespace clang 737