1 //===- unittests/libclang/LibclangTest.cpp --- libclang 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 "TestUtils.h" 10 #include "clang-c/Index.h" 11 #include "clang-c/Rewrite.h" 12 #include "llvm/ADT/StringRef.h" 13 #include "llvm/Support/Debug.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/Path.h" 16 #include "llvm/Support/raw_ostream.h" 17 #include "gtest/gtest.h" 18 #include <cstring> 19 #include <fstream> 20 #include <functional> 21 #include <map> 22 #include <memory> 23 #include <optional> 24 #include <set> 25 #define DEBUG_TYPE "libclang-test" 26 27 TEST(libclang, clang_parseTranslationUnit2_InvalidArgs) { 28 EXPECT_EQ(CXError_InvalidArguments, 29 clang_parseTranslationUnit2(nullptr, nullptr, nullptr, 0, nullptr, 30 0, 0, nullptr)); 31 } 32 33 TEST(libclang, clang_createTranslationUnit_InvalidArgs) { 34 EXPECT_EQ(nullptr, clang_createTranslationUnit(nullptr, nullptr)); 35 } 36 37 TEST(libclang, clang_createTranslationUnit2_InvalidArgs) { 38 EXPECT_EQ(CXError_InvalidArguments, 39 clang_createTranslationUnit2(nullptr, nullptr, nullptr)); 40 41 CXTranslationUnit TU = reinterpret_cast<CXTranslationUnit>(1); 42 EXPECT_EQ(CXError_InvalidArguments, 43 clang_createTranslationUnit2(nullptr, nullptr, &TU)); 44 EXPECT_EQ(nullptr, TU); 45 } 46 47 namespace { 48 struct TestVFO { 49 const char *Contents; 50 CXVirtualFileOverlay VFO; 51 52 TestVFO(const char *Contents) : Contents(Contents) { 53 VFO = clang_VirtualFileOverlay_create(0); 54 } 55 56 void map(const char *VPath, const char *RPath) { 57 CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath); 58 EXPECT_EQ(Err, CXError_Success); 59 } 60 61 void mapError(const char *VPath, const char *RPath, CXErrorCode ExpErr) { 62 CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath); 63 EXPECT_EQ(Err, ExpErr); 64 } 65 66 ~TestVFO() { 67 if (Contents) { 68 char *BufPtr; 69 unsigned BufSize; 70 clang_VirtualFileOverlay_writeToBuffer(VFO, 0, &BufPtr, &BufSize); 71 std::string BufStr(BufPtr, BufSize); 72 EXPECT_STREQ(Contents, BufStr.c_str()); 73 clang_free(BufPtr); 74 } 75 clang_VirtualFileOverlay_dispose(VFO); 76 } 77 }; 78 } 79 80 TEST(libclang, VirtualFileOverlay_Basic) { 81 const char *contents = 82 "{\n" 83 " 'version': 0,\n" 84 " 'roots': [\n" 85 " {\n" 86 " 'type': 'directory',\n" 87 " 'name': \"/path/virtual\",\n" 88 " 'contents': [\n" 89 " {\n" 90 " 'type': 'file',\n" 91 " 'name': \"foo.h\",\n" 92 " 'external-contents': \"/real/foo.h\"\n" 93 " }\n" 94 " ]\n" 95 " }\n" 96 " ]\n" 97 "}\n"; 98 TestVFO T(contents); 99 T.map("/path/virtual/foo.h", "/real/foo.h"); 100 } 101 102 TEST(libclang, VirtualFileOverlay_Unicode) { 103 const char *contents = 104 "{\n" 105 " 'version': 0,\n" 106 " 'roots': [\n" 107 " {\n" 108 " 'type': 'directory',\n" 109 " 'name': \"/path/\\u266B\",\n" 110 " 'contents': [\n" 111 " {\n" 112 " 'type': 'file',\n" 113 " 'name': \"\\u2602.h\",\n" 114 " 'external-contents': \"/real/\\u2602.h\"\n" 115 " }\n" 116 " ]\n" 117 " }\n" 118 " ]\n" 119 "}\n"; 120 TestVFO T(contents); 121 T.map("/path/♫/☂.h", "/real/☂.h"); 122 } 123 124 TEST(libclang, VirtualFileOverlay_InvalidArgs) { 125 TestVFO T(nullptr); 126 T.mapError("/path/./virtual/../foo.h", "/real/foo.h", 127 CXError_InvalidArguments); 128 } 129 130 TEST(libclang, VirtualFileOverlay_RemapDirectories) { 131 const char *contents = 132 "{\n" 133 " 'version': 0,\n" 134 " 'roots': [\n" 135 " {\n" 136 " 'type': 'directory',\n" 137 " 'name': \"/another/dir\",\n" 138 " 'contents': [\n" 139 " {\n" 140 " 'type': 'file',\n" 141 " 'name': \"foo2.h\",\n" 142 " 'external-contents': \"/real/foo2.h\"\n" 143 " }\n" 144 " ]\n" 145 " },\n" 146 " {\n" 147 " 'type': 'directory',\n" 148 " 'name': \"/path/virtual/dir\",\n" 149 " 'contents': [\n" 150 " {\n" 151 " 'type': 'file',\n" 152 " 'name': \"foo1.h\",\n" 153 " 'external-contents': \"/real/foo1.h\"\n" 154 " },\n" 155 " {\n" 156 " 'type': 'file',\n" 157 " 'name': \"foo3.h\",\n" 158 " 'external-contents': \"/real/foo3.h\"\n" 159 " },\n" 160 " {\n" 161 " 'type': 'directory',\n" 162 " 'name': \"in/subdir\",\n" 163 " 'contents': [\n" 164 " {\n" 165 " 'type': 'file',\n" 166 " 'name': \"foo4.h\",\n" 167 " 'external-contents': \"/real/foo4.h\"\n" 168 " }\n" 169 " ]\n" 170 " }\n" 171 " ]\n" 172 " }\n" 173 " ]\n" 174 "}\n"; 175 TestVFO T(contents); 176 T.map("/path/virtual/dir/foo1.h", "/real/foo1.h"); 177 T.map("/another/dir/foo2.h", "/real/foo2.h"); 178 T.map("/path/virtual/dir/foo3.h", "/real/foo3.h"); 179 T.map("/path/virtual/dir/in/subdir/foo4.h", "/real/foo4.h"); 180 } 181 182 TEST(libclang, VirtualFileOverlay_CaseInsensitive) { 183 const char *contents = 184 "{\n" 185 " 'version': 0,\n" 186 " 'case-sensitive': 'false',\n" 187 " 'roots': [\n" 188 " {\n" 189 " 'type': 'directory',\n" 190 " 'name': \"/path/virtual\",\n" 191 " 'contents': [\n" 192 " {\n" 193 " 'type': 'file',\n" 194 " 'name': \"foo.h\",\n" 195 " 'external-contents': \"/real/foo.h\"\n" 196 " }\n" 197 " ]\n" 198 " }\n" 199 " ]\n" 200 "}\n"; 201 TestVFO T(contents); 202 T.map("/path/virtual/foo.h", "/real/foo.h"); 203 clang_VirtualFileOverlay_setCaseSensitivity(T.VFO, false); 204 } 205 206 TEST(libclang, VirtualFileOverlay_SharedPrefix) { 207 const char *contents = 208 "{\n" 209 " 'version': 0,\n" 210 " 'roots': [\n" 211 " {\n" 212 " 'type': 'directory',\n" 213 " 'name': \"/path/foo\",\n" 214 " 'contents': [\n" 215 " {\n" 216 " 'type': 'file',\n" 217 " 'name': \"bar\",\n" 218 " 'external-contents': \"/real/bar\"\n" 219 " },\n" 220 " {\n" 221 " 'type': 'file',\n" 222 " 'name': \"bar.h\",\n" 223 " 'external-contents': \"/real/bar.h\"\n" 224 " }\n" 225 " ]\n" 226 " },\n" 227 " {\n" 228 " 'type': 'directory',\n" 229 " 'name': \"/path/foobar\",\n" 230 " 'contents': [\n" 231 " {\n" 232 " 'type': 'file',\n" 233 " 'name': \"baz.h\",\n" 234 " 'external-contents': \"/real/baz.h\"\n" 235 " }\n" 236 " ]\n" 237 " },\n" 238 " {\n" 239 " 'type': 'directory',\n" 240 " 'name': \"/path\",\n" 241 " 'contents': [\n" 242 " {\n" 243 " 'type': 'file',\n" 244 " 'name': \"foobarbaz.h\",\n" 245 " 'external-contents': \"/real/foobarbaz.h\"\n" 246 " }\n" 247 " ]\n" 248 " }\n" 249 " ]\n" 250 "}\n"; 251 TestVFO T(contents); 252 T.map("/path/foo/bar.h", "/real/bar.h"); 253 T.map("/path/foo/bar", "/real/bar"); 254 T.map("/path/foobar/baz.h", "/real/baz.h"); 255 T.map("/path/foobarbaz.h", "/real/foobarbaz.h"); 256 } 257 258 TEST(libclang, VirtualFileOverlay_AdjacentDirectory) { 259 const char *contents = 260 "{\n" 261 " 'version': 0,\n" 262 " 'roots': [\n" 263 " {\n" 264 " 'type': 'directory',\n" 265 " 'name': \"/path/dir1\",\n" 266 " 'contents': [\n" 267 " {\n" 268 " 'type': 'file',\n" 269 " 'name': \"foo.h\",\n" 270 " 'external-contents': \"/real/foo.h\"\n" 271 " },\n" 272 " {\n" 273 " 'type': 'directory',\n" 274 " 'name': \"subdir\",\n" 275 " 'contents': [\n" 276 " {\n" 277 " 'type': 'file',\n" 278 " 'name': \"bar.h\",\n" 279 " 'external-contents': \"/real/bar.h\"\n" 280 " }\n" 281 " ]\n" 282 " }\n" 283 " ]\n" 284 " },\n" 285 " {\n" 286 " 'type': 'directory',\n" 287 " 'name': \"/path/dir2\",\n" 288 " 'contents': [\n" 289 " {\n" 290 " 'type': 'file',\n" 291 " 'name': \"baz.h\",\n" 292 " 'external-contents': \"/real/baz.h\"\n" 293 " }\n" 294 " ]\n" 295 " }\n" 296 " ]\n" 297 "}\n"; 298 TestVFO T(contents); 299 T.map("/path/dir1/foo.h", "/real/foo.h"); 300 T.map("/path/dir1/subdir/bar.h", "/real/bar.h"); 301 T.map("/path/dir2/baz.h", "/real/baz.h"); 302 } 303 304 TEST(libclang, VirtualFileOverlay_TopLevel) { 305 const char *contents = 306 "{\n" 307 " 'version': 0,\n" 308 " 'roots': [\n" 309 " {\n" 310 " 'type': 'directory',\n" 311 " 'name': \"/\",\n" 312 " 'contents': [\n" 313 " {\n" 314 " 'type': 'file',\n" 315 " 'name': \"foo.h\",\n" 316 " 'external-contents': \"/real/foo.h\"\n" 317 " }\n" 318 " ]\n" 319 " }\n" 320 " ]\n" 321 "}\n"; 322 TestVFO T(contents); 323 T.map("/foo.h", "/real/foo.h"); 324 } 325 326 TEST(libclang, VirtualFileOverlay_Empty) { 327 const char *contents = 328 "{\n" 329 " 'version': 0,\n" 330 " 'roots': [\n" 331 " ]\n" 332 "}\n"; 333 TestVFO T(contents); 334 } 335 336 TEST(libclang, ModuleMapDescriptor) { 337 const char *Contents = 338 "framework module TestFrame {\n" 339 " umbrella header \"TestFrame.h\"\n" 340 "\n" 341 " export *\n" 342 " module * { export * }\n" 343 "}\n"; 344 345 CXModuleMapDescriptor MMD = clang_ModuleMapDescriptor_create(0); 346 347 clang_ModuleMapDescriptor_setFrameworkModuleName(MMD, "TestFrame"); 348 clang_ModuleMapDescriptor_setUmbrellaHeader(MMD, "TestFrame.h"); 349 350 char *BufPtr; 351 unsigned BufSize; 352 clang_ModuleMapDescriptor_writeToBuffer(MMD, 0, &BufPtr, &BufSize); 353 std::string BufStr(BufPtr, BufSize); 354 EXPECT_STREQ(Contents, BufStr.c_str()); 355 clang_free(BufPtr); 356 clang_ModuleMapDescriptor_dispose(MMD); 357 } 358 359 TEST_F(LibclangParseTest, GlobalOptions) { 360 EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None); 361 } 362 363 class LibclangIndexOptionsTest : public LibclangParseTest { 364 virtual void AdjustOptions(CXIndexOptions &Opts) {} 365 366 protected: 367 void CreateIndex() override { 368 CXIndexOptions Opts; 369 memset(&Opts, 0, sizeof(Opts)); 370 Opts.Size = sizeof(CXIndexOptions); 371 AdjustOptions(Opts); 372 Index = clang_createIndexWithOptions(&Opts); 373 ASSERT_TRUE(Index); 374 } 375 }; 376 377 TEST_F(LibclangIndexOptionsTest, GlobalOptions) { 378 EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None); 379 } 380 381 class LibclangIndexingEnabledIndexOptionsTest 382 : public LibclangIndexOptionsTest { 383 void AdjustOptions(CXIndexOptions &Opts) override { 384 Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled; 385 } 386 }; 387 388 TEST_F(LibclangIndexingEnabledIndexOptionsTest, GlobalOptions) { 389 EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), 390 CXGlobalOpt_ThreadBackgroundPriorityForIndexing); 391 } 392 393 class LibclangIndexingDisabledEditingEnabledIndexOptionsTest 394 : public LibclangIndexOptionsTest { 395 void AdjustOptions(CXIndexOptions &Opts) override { 396 Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Disabled; 397 Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled; 398 } 399 }; 400 401 TEST_F(LibclangIndexingDisabledEditingEnabledIndexOptionsTest, GlobalOptions) { 402 EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), 403 CXGlobalOpt_ThreadBackgroundPriorityForEditing); 404 } 405 406 class LibclangBothEnabledIndexOptionsTest : public LibclangIndexOptionsTest { 407 void AdjustOptions(CXIndexOptions &Opts) override { 408 Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled; 409 Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled; 410 } 411 }; 412 413 TEST_F(LibclangBothEnabledIndexOptionsTest, GlobalOptions) { 414 EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), 415 CXGlobalOpt_ThreadBackgroundPriorityForAll); 416 } 417 418 class LibclangPreambleStorageTest : public LibclangParseTest { 419 std::string Main = "main.cpp"; 420 421 protected: 422 std::string PreambleDir; 423 void InitializePreambleDir() { 424 llvm::SmallString<128> PathBuffer(TestDir); 425 llvm::sys::path::append(PathBuffer, "preambles"); 426 namespace fs = llvm::sys::fs; 427 ASSERT_FALSE(fs::create_directory(PathBuffer, false, fs::perms::owner_all)); 428 429 PreambleDir = static_cast<std::string>(PathBuffer); 430 FilesAndDirsToRemove.insert(PreambleDir); 431 } 432 433 public: 434 void CountPreamblesInPreambleDir(int PreambleCount) { 435 // For some reason, the preamble is not created without '\n' before `int`. 436 WriteFile(Main, "\nint main() {}"); 437 438 TUFlags |= CXTranslationUnit_CreatePreambleOnFirstParse; 439 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, 440 nullptr, 0, TUFlags); 441 442 int FileCount = 0; 443 444 namespace fs = llvm::sys::fs; 445 std::error_code EC; 446 for (fs::directory_iterator File(PreambleDir, EC), FileEnd; 447 File != FileEnd && !EC; File.increment(EC)) { 448 ++FileCount; 449 450 EXPECT_EQ(File->type(), fs::file_type::regular_file); 451 452 const auto Filename = llvm::sys::path::filename(File->path()); 453 EXPECT_EQ(Filename.size(), std::strlen("preamble-%%%%%%.pch")); 454 EXPECT_TRUE(Filename.startswith("preamble-")); 455 EXPECT_TRUE(Filename.endswith(".pch")); 456 457 const auto Status = File->status(); 458 ASSERT_TRUE(Status); 459 if (false) { 460 // The permissions assertion below fails, because the .pch.tmp file is 461 // created with default permissions and replaces the .pch file along 462 // with its permissions. Therefore the permissions set in 463 // TempPCHFile::create() don't matter in the end. 464 EXPECT_EQ(Status->permissions(), fs::owner_read | fs::owner_write); 465 } 466 } 467 468 EXPECT_EQ(FileCount, PreambleCount); 469 } 470 }; 471 472 class LibclangNotOverriddenPreambleStoragePathTest 473 : public LibclangPreambleStorageTest { 474 protected: 475 void CreateIndex() override { 476 InitializePreambleDir(); 477 LibclangPreambleStorageTest::CreateIndex(); 478 } 479 }; 480 481 class LibclangSetPreambleStoragePathTest : public LibclangPreambleStorageTest { 482 virtual bool StorePreamblesInMemory() { return false; } 483 virtual const char *PreambleStoragePath() = 0; 484 485 protected: 486 void CreateIndex() override { 487 InitializePreambleDir(); 488 489 CXIndexOptions Opts{}; 490 Opts.Size = sizeof(CXIndexOptions); 491 Opts.StorePreamblesInMemory = StorePreamblesInMemory(); 492 Opts.PreambleStoragePath = PreambleStoragePath(); 493 Index = clang_createIndexWithOptions(&Opts); 494 ASSERT_TRUE(Index); 495 } 496 }; 497 498 class LibclangNullPreambleStoragePathTest 499 : public LibclangSetPreambleStoragePathTest { 500 const char *PreambleStoragePath() override { return nullptr; } 501 }; 502 class LibclangEmptyPreambleStoragePathTest 503 : public LibclangSetPreambleStoragePathTest { 504 const char *PreambleStoragePath() override { return ""; } 505 }; 506 class LibclangPreambleDirPreambleStoragePathTest 507 : public LibclangSetPreambleStoragePathTest { 508 const char *PreambleStoragePath() override { return PreambleDir.c_str(); } 509 }; 510 511 class LibclangStoreInMemoryNullPreambleStoragePathTest 512 : public LibclangNullPreambleStoragePathTest { 513 bool StorePreamblesInMemory() override { return true; } 514 }; 515 class LibclangStoreInMemoryEmptyPreambleStoragePathTest 516 : public LibclangEmptyPreambleStoragePathTest { 517 bool StorePreamblesInMemory() override { return true; } 518 }; 519 class LibclangStoreInMemoryPreambleDirPreambleStoragePathTest 520 : public LibclangPreambleDirPreambleStoragePathTest { 521 bool StorePreamblesInMemory() override { return true; } 522 }; 523 524 TEST_F(LibclangNotOverriddenPreambleStoragePathTest, CountPreambles) { 525 CountPreamblesInPreambleDir(0); 526 } 527 TEST_F(LibclangNullPreambleStoragePathTest, CountPreambles) { 528 CountPreamblesInPreambleDir(0); 529 } 530 TEST_F(LibclangEmptyPreambleStoragePathTest, CountPreambles) { 531 CountPreamblesInPreambleDir(0); 532 } 533 TEST_F(LibclangPreambleDirPreambleStoragePathTest, CountPreambles) { 534 CountPreamblesInPreambleDir(1); 535 } 536 TEST_F(LibclangStoreInMemoryNullPreambleStoragePathTest, CountPreambles) { 537 CountPreamblesInPreambleDir(0); 538 } 539 TEST_F(LibclangStoreInMemoryEmptyPreambleStoragePathTest, CountPreambles) { 540 CountPreamblesInPreambleDir(0); 541 } 542 TEST_F(LibclangStoreInMemoryPreambleDirPreambleStoragePathTest, 543 CountPreambles) { 544 CountPreamblesInPreambleDir(0); 545 } 546 547 TEST_F(LibclangParseTest, AllSkippedRanges) { 548 std::string Header = "header.h", Main = "main.cpp"; 549 WriteFile(Header, 550 "#ifdef MANGOS\n" 551 "printf(\"mmm\");\n" 552 "#endif"); 553 WriteFile(Main, 554 "#include \"header.h\"\n" 555 "#ifdef KIWIS\n" 556 "printf(\"mmm!!\");\n" 557 "#endif"); 558 559 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, 560 nullptr, 0, TUFlags); 561 562 CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU); 563 EXPECT_EQ(2U, Ranges->count); 564 565 CXSourceLocation cxl; 566 unsigned line; 567 cxl = clang_getRangeStart(Ranges->ranges[0]); 568 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 569 EXPECT_EQ(1U, line); 570 cxl = clang_getRangeEnd(Ranges->ranges[0]); 571 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 572 EXPECT_EQ(3U, line); 573 574 cxl = clang_getRangeStart(Ranges->ranges[1]); 575 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 576 EXPECT_EQ(2U, line); 577 cxl = clang_getRangeEnd(Ranges->ranges[1]); 578 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 579 EXPECT_EQ(4U, line); 580 581 clang_disposeSourceRangeList(Ranges); 582 } 583 584 TEST_F(LibclangParseTest, EvaluateChildExpression) { 585 std::string Main = "main.m"; 586 WriteFile(Main, "#define kFOO @\"foo\"\n" 587 "void foobar(void) {\n" 588 " {kFOO;}\n" 589 "}\n"); 590 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 591 0, TUFlags); 592 593 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 594 clang_visitChildren( 595 C, 596 [](CXCursor cursor, CXCursor parent, 597 CXClientData client_data) -> CXChildVisitResult { 598 if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) { 599 int numberedStmt = 0; 600 clang_visitChildren( 601 cursor, 602 [](CXCursor cursor, CXCursor parent, 603 CXClientData client_data) -> CXChildVisitResult { 604 int &numberedStmt = *((int *)client_data); 605 if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt) { 606 if (numberedStmt) { 607 CXEvalResult RE = clang_Cursor_Evaluate(cursor); 608 EXPECT_NE(RE, nullptr); 609 EXPECT_EQ(clang_EvalResult_getKind(RE), 610 CXEval_ObjCStrLiteral); 611 clang_EvalResult_dispose(RE); 612 return CXChildVisit_Break; 613 } 614 numberedStmt++; 615 } 616 return CXChildVisit_Recurse; 617 }, 618 &numberedStmt); 619 EXPECT_EQ(numberedStmt, 1); 620 } 621 return CXChildVisit_Continue; 622 }, 623 nullptr); 624 } 625 626 class LibclangReparseTest : public LibclangParseTest { 627 public: 628 void DisplayDiagnostics() { 629 unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU); 630 for (unsigned i = 0; i < NumDiagnostics; ++i) { 631 auto Diag = clang_getDiagnostic(ClangTU, i); 632 LLVM_DEBUG(llvm::dbgs() 633 << clang_getCString(clang_formatDiagnostic( 634 Diag, clang_defaultDiagnosticDisplayOptions())) 635 << "\n"); 636 clang_disposeDiagnostic(Diag); 637 } 638 } 639 bool ReparseTU(unsigned num_unsaved_files, CXUnsavedFile* unsaved_files) { 640 if (clang_reparseTranslationUnit(ClangTU, num_unsaved_files, unsaved_files, 641 clang_defaultReparseOptions(ClangTU))) { 642 LLVM_DEBUG(llvm::dbgs() << "Reparse failed\n"); 643 return false; 644 } 645 DisplayDiagnostics(); 646 return true; 647 } 648 }; 649 650 TEST_F(LibclangReparseTest, FileName) { 651 std::string CppName = "main.cpp"; 652 WriteFile(CppName, "int main() {}"); 653 ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0, 654 nullptr, 0, TUFlags); 655 CXFile cxf = clang_getFile(ClangTU, CppName.c_str()); 656 657 CXString cxname = clang_getFileName(cxf); 658 ASSERT_STREQ(clang_getCString(cxname), CppName.c_str()); 659 clang_disposeString(cxname); 660 661 cxname = clang_File_tryGetRealPathName(cxf); 662 ASSERT_TRUE(llvm::StringRef(clang_getCString(cxname)).endswith("main.cpp")); 663 clang_disposeString(cxname); 664 } 665 666 TEST_F(LibclangReparseTest, Reparse) { 667 const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;"; 668 const char *HeaderBottom = "\n};\n#endif\n"; 669 const char *CppFile = "#include \"HeaderFile.h\"\nint main() {" 670 " Foo foo; foo.bar = 7; foo.baz = 8; }\n"; 671 std::string HeaderName = "HeaderFile.h"; 672 std::string CppName = "CppFile.cpp"; 673 WriteFile(CppName, CppFile); 674 WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom); 675 676 ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0, 677 nullptr, 0, TUFlags); 678 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU)); 679 DisplayDiagnostics(); 680 681 // Immedaitely reparse. 682 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); 683 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU)); 684 685 std::string NewHeaderContents = 686 std::string(HeaderTop) + "int baz;" + HeaderBottom; 687 WriteFile(HeaderName, NewHeaderContents); 688 689 // Reparse after fix. 690 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); 691 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU)); 692 } 693 694 TEST_F(LibclangReparseTest, ReparseWithModule) { 695 const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;"; 696 const char *HeaderBottom = "\n};\n#endif\n"; 697 const char *MFile = "#include \"HeaderFile.h\"\nint main() {" 698 " struct Foo foo; foo.bar = 7; foo.baz = 8; }\n"; 699 const char *ModFile = "module A { header \"HeaderFile.h\" }\n"; 700 std::string HeaderName = "HeaderFile.h"; 701 std::string MName = "MFile.m"; 702 std::string ModName = "module.modulemap"; 703 WriteFile(MName, MFile); 704 WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom); 705 WriteFile(ModName, ModFile); 706 707 // Removing recursively is necessary to delete the module cache. 708 RemoveTestDirRecursivelyDuringTeardown = true; 709 std::string ModulesCache = std::string("-fmodules-cache-path=") + TestDir; 710 const char *Args[] = { "-fmodules", ModulesCache.c_str(), 711 "-I", TestDir.c_str() }; 712 int NumArgs = std::size(Args); 713 ClangTU = clang_parseTranslationUnit(Index, MName.c_str(), Args, NumArgs, 714 nullptr, 0, TUFlags); 715 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU)); 716 DisplayDiagnostics(); 717 718 // Immedaitely reparse. 719 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); 720 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU)); 721 722 std::string NewHeaderContents = 723 std::string(HeaderTop) + "int baz;" + HeaderBottom; 724 WriteFile(HeaderName, NewHeaderContents); 725 726 // Reparse after fix. 727 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); 728 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU)); 729 } 730 731 TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) { 732 // Provide a fake GCC 99.9.9 standard library that always overrides any local 733 // GCC installation. 734 std::string EmptyFiles[] = {"lib/gcc/arm-linux-gnueabi/99.9.9/crtbegin.o", 735 "include/arm-linux-gnueabi/.keep", 736 "include/c++/99.9.9/vector"}; 737 738 for (auto &Name : EmptyFiles) 739 WriteFile(Name, "\n"); 740 741 std::string Filename = "test.cc"; 742 WriteFile(Filename, "#include <vector>\n"); 743 744 std::string Clang = "bin/clang"; 745 WriteFile(Clang, ""); 746 747 const char *Argv[] = {Clang.c_str(), "-target", "arm-linux-gnueabi", 748 "-stdlib=libstdc++", "--gcc-toolchain="}; 749 750 EXPECT_EQ(CXError_Success, 751 clang_parseTranslationUnit2FullArgv(Index, Filename.c_str(), Argv, 752 std::size(Argv), 753 nullptr, 0, TUFlags, &ClangTU)); 754 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU)); 755 DisplayDiagnostics(); 756 } 757 758 class LibclangPrintingPolicyTest : public LibclangParseTest { 759 public: 760 CXPrintingPolicy Policy = nullptr; 761 762 void SetUp() override { 763 LibclangParseTest::SetUp(); 764 std::string File = "file.cpp"; 765 WriteFile(File, "int i;\n"); 766 ClangTU = clang_parseTranslationUnit(Index, File.c_str(), nullptr, 0, 767 nullptr, 0, TUFlags); 768 CXCursor TUCursor = clang_getTranslationUnitCursor(ClangTU); 769 Policy = clang_getCursorPrintingPolicy(TUCursor); 770 } 771 void TearDown() override { 772 clang_PrintingPolicy_dispose(Policy); 773 LibclangParseTest::TearDown(); 774 } 775 }; 776 777 TEST_F(LibclangPrintingPolicyTest, SetAndGetProperties) { 778 for (unsigned Value = 0; Value < 2; ++Value) { 779 for (int I = 0; I < CXPrintingPolicy_LastProperty; ++I) { 780 auto Property = static_cast<enum CXPrintingPolicyProperty>(I); 781 782 clang_PrintingPolicy_setProperty(Policy, Property, Value); 783 EXPECT_EQ(Value, clang_PrintingPolicy_getProperty(Policy, Property)); 784 } 785 } 786 } 787 788 TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) { 789 std::string Header = "header.h", Main = "main.cpp"; 790 WriteFile(Header, 791 "#ifdef MANGOS\n" 792 "printf(\"mmm\");\n" 793 "#endif"); 794 WriteFile(Main, 795 "#include \"header.h\"\n" 796 "#ifdef GUAVA\n" 797 "#endif\n" 798 "#ifdef KIWIS\n" 799 "printf(\"mmm!!\");\n" 800 "#endif"); 801 802 for (int i = 0; i != 3; ++i) { 803 unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble; 804 if (i == 2) 805 flags |= CXTranslationUnit_CreatePreambleOnFirstParse; 806 807 if (i != 0) 808 clang_disposeTranslationUnit(ClangTU); // dispose from previous iter 809 810 // parse once 811 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, 812 nullptr, 0, flags); 813 if (i != 0) { 814 // reparse 815 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); 816 } 817 818 // Check all ranges are there 819 CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU); 820 EXPECT_EQ(3U, Ranges->count); 821 822 CXSourceLocation cxl; 823 unsigned line; 824 cxl = clang_getRangeStart(Ranges->ranges[0]); 825 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 826 EXPECT_EQ(1U, line); 827 cxl = clang_getRangeEnd(Ranges->ranges[0]); 828 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 829 EXPECT_EQ(3U, line); 830 831 cxl = clang_getRangeStart(Ranges->ranges[1]); 832 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 833 EXPECT_EQ(2U, line); 834 cxl = clang_getRangeEnd(Ranges->ranges[1]); 835 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 836 EXPECT_EQ(3U, line); 837 838 cxl = clang_getRangeStart(Ranges->ranges[2]); 839 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 840 EXPECT_EQ(4U, line); 841 cxl = clang_getRangeEnd(Ranges->ranges[2]); 842 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 843 EXPECT_EQ(6U, line); 844 845 clang_disposeSourceRangeList(Ranges); 846 847 // Check obtaining ranges by each file works 848 CXFile cxf = clang_getFile(ClangTU, Header.c_str()); 849 Ranges = clang_getSkippedRanges(ClangTU, cxf); 850 EXPECT_EQ(1U, Ranges->count); 851 cxl = clang_getRangeStart(Ranges->ranges[0]); 852 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 853 EXPECT_EQ(1U, line); 854 clang_disposeSourceRangeList(Ranges); 855 856 cxf = clang_getFile(ClangTU, Main.c_str()); 857 Ranges = clang_getSkippedRanges(ClangTU, cxf); 858 EXPECT_EQ(2U, Ranges->count); 859 cxl = clang_getRangeStart(Ranges->ranges[0]); 860 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 861 EXPECT_EQ(2U, line); 862 cxl = clang_getRangeStart(Ranges->ranges[1]); 863 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); 864 EXPECT_EQ(4U, line); 865 clang_disposeSourceRangeList(Ranges); 866 } 867 } 868 869 class LibclangSerializationTest : public LibclangParseTest { 870 public: 871 bool SaveAndLoadTU(const std::string &Filename) { 872 unsigned options = clang_defaultSaveOptions(ClangTU); 873 if (clang_saveTranslationUnit(ClangTU, Filename.c_str(), options) != 874 CXSaveError_None) { 875 LLVM_DEBUG(llvm::dbgs() << "Saving failed\n"); 876 return false; 877 } 878 879 clang_disposeTranslationUnit(ClangTU); 880 881 ClangTU = clang_createTranslationUnit(Index, Filename.c_str()); 882 883 if (!ClangTU) { 884 LLVM_DEBUG(llvm::dbgs() << "Loading failed\n"); 885 return false; 886 } 887 888 return true; 889 } 890 }; 891 892 TEST_F(LibclangSerializationTest, TokenKindsAreCorrectAfterLoading) { 893 // Ensure that "class" is recognized as a keyword token after serializing 894 // and reloading the AST, as it is not a keyword for the default LangOptions. 895 std::string HeaderName = "test.h"; 896 WriteFile(HeaderName, "enum class Something {};"); 897 898 const char *Argv[] = {"-xc++-header", "-std=c++11"}; 899 900 ClangTU = clang_parseTranslationUnit(Index, HeaderName.c_str(), Argv, 901 std::size(Argv), nullptr, 902 0, TUFlags); 903 904 auto CheckTokenKinds = [=]() { 905 CXSourceRange Range = 906 clang_getCursorExtent(clang_getTranslationUnitCursor(ClangTU)); 907 908 CXToken *Tokens; 909 unsigned int NumTokens; 910 clang_tokenize(ClangTU, Range, &Tokens, &NumTokens); 911 912 ASSERT_EQ(6u, NumTokens); 913 EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[0])); 914 EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[1])); 915 EXPECT_EQ(CXToken_Identifier, clang_getTokenKind(Tokens[2])); 916 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[3])); 917 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[4])); 918 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[5])); 919 920 clang_disposeTokens(ClangTU, Tokens, NumTokens); 921 }; 922 923 CheckTokenKinds(); 924 925 std::string ASTName = "test.ast"; 926 WriteFile(ASTName, ""); 927 928 ASSERT_TRUE(SaveAndLoadTU(ASTName)); 929 930 CheckTokenKinds(); 931 } 932 933 TEST_F(LibclangParseTest, clang_getVarDeclInitializer) { 934 std::string Main = "main.cpp"; 935 WriteFile(Main, "int foo() { return 5; }; const int a = foo();"); 936 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 937 0, TUFlags); 938 939 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 940 clang_visitChildren( 941 C, 942 [](CXCursor cursor, CXCursor parent, 943 CXClientData client_data) -> CXChildVisitResult { 944 if (clang_getCursorKind(cursor) == CXCursor_VarDecl) { 945 const CXCursor Initializer = clang_Cursor_getVarDeclInitializer(cursor); 946 EXPECT_FALSE(clang_Cursor_isNull(Initializer)); 947 CXString Spelling = clang_getCursorSpelling(Initializer); 948 const char* const SpellingCSstr = clang_getCString(Spelling); 949 EXPECT_TRUE(SpellingCSstr); 950 EXPECT_EQ(std::string(SpellingCSstr), std::string("foo")); 951 clang_disposeString(Spelling); 952 return CXChildVisit_Break; 953 } 954 return CXChildVisit_Continue; 955 }, 956 nullptr); 957 } 958 959 TEST_F(LibclangParseTest, clang_hasVarDeclGlobalStorageFalse) { 960 std::string Main = "main.cpp"; 961 WriteFile(Main, "void foo() { int a; }"); 962 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 963 0, TUFlags); 964 965 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 966 clang_visitChildren( 967 C, 968 [](CXCursor cursor, CXCursor parent, 969 CXClientData client_data) -> CXChildVisitResult { 970 if (clang_getCursorKind(cursor) == CXCursor_VarDecl) { 971 EXPECT_FALSE(clang_Cursor_hasVarDeclGlobalStorage(cursor)); 972 return CXChildVisit_Break; 973 } 974 return CXChildVisit_Continue; 975 }, 976 nullptr); 977 } 978 979 TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclGlobalStorageTrue) { 980 std::string Main = "main.cpp"; 981 WriteFile(Main, "int a;"); 982 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 983 0, TUFlags); 984 985 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 986 clang_visitChildren( 987 C, 988 [](CXCursor cursor, CXCursor parent, 989 CXClientData client_data) -> CXChildVisitResult { 990 if (clang_getCursorKind(cursor) == CXCursor_VarDecl) { 991 EXPECT_TRUE(clang_Cursor_hasVarDeclGlobalStorage(cursor)); 992 return CXChildVisit_Break; 993 } 994 return CXChildVisit_Continue; 995 }, 996 nullptr); 997 } 998 999 TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageFalse) { 1000 std::string Main = "main.cpp"; 1001 WriteFile(Main, "int a;"); 1002 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 1003 0, TUFlags); 1004 1005 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 1006 clang_visitChildren( 1007 C, 1008 [](CXCursor cursor, CXCursor parent, 1009 CXClientData client_data) -> CXChildVisitResult { 1010 if (clang_getCursorKind(cursor) == CXCursor_VarDecl) { 1011 EXPECT_FALSE(clang_Cursor_hasVarDeclExternalStorage(cursor)); 1012 return CXChildVisit_Break; 1013 } 1014 return CXChildVisit_Continue; 1015 }, 1016 nullptr); 1017 } 1018 1019 TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageTrue) { 1020 std::string Main = "main.cpp"; 1021 WriteFile(Main, "extern int a;"); 1022 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 1023 0, TUFlags); 1024 1025 CXCursor C = clang_getTranslationUnitCursor(ClangTU); 1026 clang_visitChildren( 1027 C, 1028 [](CXCursor cursor, CXCursor parent, 1029 CXClientData client_data) -> CXChildVisitResult { 1030 if (clang_getCursorKind(cursor) == CXCursor_VarDecl) { 1031 EXPECT_TRUE(clang_Cursor_hasVarDeclExternalStorage(cursor)); 1032 return CXChildVisit_Break; 1033 } 1034 return CXChildVisit_Continue; 1035 }, 1036 nullptr); 1037 } 1038 1039 TEST_F(LibclangParseTest, clang_getUnqualifiedTypeRemovesQualifiers) { 1040 std::string Header = "header.h"; 1041 WriteFile(Header, "void foo1(const int);\n" 1042 "void foo2(volatile int);\n" 1043 "void foo3(const volatile int);\n" 1044 "void foo4(int* const);\n" 1045 "void foo5(int* volatile);\n" 1046 "void foo6(int* restrict);\n" 1047 "void foo7(int* const volatile);\n" 1048 "void foo8(int* volatile restrict);\n" 1049 "void foo9(int* const restrict);\n" 1050 "void foo10(int* const volatile restrict);\n"); 1051 1052 auto is_qualified = [](CXType type) -> bool { 1053 return clang_isConstQualifiedType(type) || 1054 clang_isVolatileQualifiedType(type) || 1055 clang_isRestrictQualifiedType(type); 1056 }; 1057 1058 ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), nullptr, 0, 1059 nullptr, 0, TUFlags); 1060 1061 Traverse([&is_qualified](CXCursor cursor, CXCursor) { 1062 if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) { 1063 CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0); 1064 EXPECT_TRUE(is_qualified(arg_type)) 1065 << "Input data '" << fromCXString(clang_getCursorSpelling(cursor)) 1066 << "' first argument does not have a qualified type."; 1067 1068 CXType unqualified_arg_type = clang_getUnqualifiedType(arg_type); 1069 EXPECT_FALSE(is_qualified(unqualified_arg_type)) 1070 << "The type '" << fromCXString(clang_getTypeSpelling(arg_type)) 1071 << "' was not unqualified after a call to clang_getUnqualifiedType."; 1072 } 1073 1074 return CXChildVisit_Continue; 1075 }); 1076 } 1077 1078 TEST_F(LibclangParseTest, clang_getNonReferenceTypeRemovesRefQualifiers) { 1079 std::string Header = "header.h"; 1080 WriteFile(Header, "void foo1(int&);\n" 1081 "void foo2(int&&);\n"); 1082 1083 auto is_ref_qualified = [](CXType type) -> bool { 1084 return (type.kind == CXType_LValueReference) || 1085 (type.kind == CXType_RValueReference); 1086 }; 1087 1088 const char *Args[] = {"-xc++"}; 1089 ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), Args, 1, nullptr, 1090 0, TUFlags); 1091 1092 Traverse([&is_ref_qualified](CXCursor cursor, CXCursor) { 1093 if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) { 1094 CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0); 1095 EXPECT_TRUE(is_ref_qualified(arg_type)) 1096 << "Input data '" << fromCXString(clang_getCursorSpelling(cursor)) 1097 << "' first argument does not have a ref-qualified type."; 1098 1099 CXType non_reference_arg_type = clang_getNonReferenceType(arg_type); 1100 EXPECT_FALSE(is_ref_qualified(non_reference_arg_type)) 1101 << "The type '" << fromCXString(clang_getTypeSpelling(arg_type)) 1102 << "' ref-qualifier was not removed after a call to " 1103 "clang_getNonReferenceType."; 1104 } 1105 1106 return CXChildVisit_Continue; 1107 }); 1108 } 1109 1110 TEST_F(LibclangParseTest, VisitUsingTypeLoc) { 1111 const char testSource[] = R"cpp( 1112 namespace ns1 { 1113 class Class1 1114 { 1115 void fun(); 1116 }; 1117 } 1118 1119 using ns1::Class1; 1120 1121 void Class1::fun() {} 1122 )cpp"; 1123 std::string fileName = "main.cpp"; 1124 WriteFile(fileName, testSource); 1125 const char *Args[] = {"-xc++"}; 1126 ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1, 1127 nullptr, 0, TUFlags); 1128 1129 std::optional<CXCursor> typeRefCsr; 1130 Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { 1131 if (cursor.kind == CXCursor_TypeRef) { 1132 typeRefCsr.emplace(cursor); 1133 } 1134 return CXChildVisit_Recurse; 1135 }); 1136 ASSERT_TRUE(typeRefCsr.has_value()); 1137 EXPECT_EQ(fromCXString(clang_getCursorSpelling(*typeRefCsr)), 1138 "class ns1::Class1"); 1139 } 1140 1141 TEST_F(LibclangParseTest, BinaryOperator) { 1142 std::string Main = "main.cpp"; 1143 WriteFile(Main, "int foo() { return 5 + 9; }"); 1144 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 1145 0, TUFlags); 1146 1147 Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { 1148 if (cursor.kind == CXCursor_BinaryOperator) { 1149 EXPECT_EQ(clang_getCursorBinaryOperatorKind(cursor), 1150 CXBinaryOperator_Add); 1151 return CXChildVisit_Break; 1152 } 1153 1154 return CXChildVisit_Recurse; 1155 }); 1156 } 1157 1158 TEST_F(LibclangParseTest, UnaryOperator) { 1159 std::string Main = "main.cpp"; 1160 WriteFile(Main, "int foo() { int a = 5; return a++; }"); 1161 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 1162 0, TUFlags); 1163 1164 Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { 1165 if (cursor.kind == CXCursor_UnaryOperator) { 1166 EXPECT_EQ(clang_getCursorUnaryOperatorKind(cursor), 1167 CXUnaryOperator_PostInc); 1168 return CXChildVisit_Break; 1169 } 1170 1171 return CXChildVisit_Recurse; 1172 }); 1173 } 1174 1175 class LibclangRewriteTest : public LibclangParseTest { 1176 public: 1177 CXRewriter Rew = nullptr; 1178 std::string Filename; 1179 CXFile File = nullptr; 1180 1181 void SetUp() override { 1182 LibclangParseTest::SetUp(); 1183 Filename = "file.cpp"; 1184 WriteFile(Filename, "int main() { return 0; }"); 1185 ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0, 1186 nullptr, 0, TUFlags); 1187 Rew = clang_CXRewriter_create(ClangTU); 1188 File = clang_getFile(ClangTU, Filename.c_str()); 1189 } 1190 void TearDown() override { 1191 clang_CXRewriter_dispose(Rew); 1192 LibclangParseTest::TearDown(); 1193 } 1194 }; 1195 1196 static std::string getFileContent(const std::string& Filename) { 1197 std::ifstream RewrittenFile(Filename); 1198 std::string RewrittenFileContent; 1199 std::string Line; 1200 while (std::getline(RewrittenFile, Line)) { 1201 if (RewrittenFileContent.empty()) 1202 RewrittenFileContent = Line; 1203 else { 1204 RewrittenFileContent += "\n" + Line; 1205 } 1206 } 1207 return RewrittenFileContent; 1208 } 1209 1210 TEST_F(LibclangRewriteTest, RewriteReplace) { 1211 CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5); 1212 CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9); 1213 CXSourceRange Rng = clang_getRange(B, E); 1214 1215 clang_CXRewriter_replaceText(Rew, Rng, "MAIN"); 1216 1217 ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0); 1218 EXPECT_EQ(getFileContent(Filename), "int MAIN() { return 0; }"); 1219 } 1220 1221 TEST_F(LibclangRewriteTest, RewriteReplaceShorter) { 1222 CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5); 1223 CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9); 1224 CXSourceRange Rng = clang_getRange(B, E); 1225 1226 clang_CXRewriter_replaceText(Rew, Rng, "foo"); 1227 1228 ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0); 1229 EXPECT_EQ(getFileContent(Filename), "int foo() { return 0; }"); 1230 } 1231 1232 TEST_F(LibclangRewriteTest, RewriteReplaceLonger) { 1233 CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5); 1234 CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9); 1235 CXSourceRange Rng = clang_getRange(B, E); 1236 1237 clang_CXRewriter_replaceText(Rew, Rng, "patatino"); 1238 1239 ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0); 1240 EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }"); 1241 } 1242 1243 TEST_F(LibclangRewriteTest, RewriteInsert) { 1244 CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5); 1245 1246 clang_CXRewriter_insertTextBefore(Rew, Loc, "ro"); 1247 1248 ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0); 1249 EXPECT_EQ(getFileContent(Filename), "int romain() { return 0; }"); 1250 } 1251 1252 TEST_F(LibclangRewriteTest, RewriteRemove) { 1253 CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5); 1254 CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9); 1255 CXSourceRange Rng = clang_getRange(B, E); 1256 1257 clang_CXRewriter_removeText(Rew, Rng); 1258 1259 ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0); 1260 EXPECT_EQ(getFileContent(Filename), "int () { return 0; }"); 1261 } 1262