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