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 "llvm/ADT/StringRef.h"
11 #include "llvm/Support/Debug.h"
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/raw_ostream.h"
15 #include "gtest/gtest.h"
16 #include "TestUtils.h"
17 #include <fstream>
18 #include <functional>
19 #include <map>
20 #include <memory>
21 #include <set>
22 #define DEBUG_TYPE "libclang-test"
23
TEST(libclang,clang_parseTranslationUnit2_InvalidArgs)24 TEST(libclang, clang_parseTranslationUnit2_InvalidArgs) {
25 EXPECT_EQ(CXError_InvalidArguments,
26 clang_parseTranslationUnit2(nullptr, nullptr, nullptr, 0, nullptr,
27 0, 0, nullptr));
28 }
29
TEST(libclang,clang_createTranslationUnit_InvalidArgs)30 TEST(libclang, clang_createTranslationUnit_InvalidArgs) {
31 EXPECT_EQ(nullptr, clang_createTranslationUnit(nullptr, nullptr));
32 }
33
TEST(libclang,clang_createTranslationUnit2_InvalidArgs)34 TEST(libclang, clang_createTranslationUnit2_InvalidArgs) {
35 EXPECT_EQ(CXError_InvalidArguments,
36 clang_createTranslationUnit2(nullptr, nullptr, nullptr));
37
38 CXTranslationUnit TU = reinterpret_cast<CXTranslationUnit>(1);
39 EXPECT_EQ(CXError_InvalidArguments,
40 clang_createTranslationUnit2(nullptr, nullptr, &TU));
41 EXPECT_EQ(nullptr, TU);
42 }
43
44 namespace {
45 struct TestVFO {
46 const char *Contents;
47 CXVirtualFileOverlay VFO;
48
TestVFO__anonf726ac0b0111::TestVFO49 TestVFO(const char *Contents) : Contents(Contents) {
50 VFO = clang_VirtualFileOverlay_create(0);
51 }
52
map__anonf726ac0b0111::TestVFO53 void map(const char *VPath, const char *RPath) {
54 CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
55 EXPECT_EQ(Err, CXError_Success);
56 }
57
mapError__anonf726ac0b0111::TestVFO58 void mapError(const char *VPath, const char *RPath, CXErrorCode ExpErr) {
59 CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
60 EXPECT_EQ(Err, ExpErr);
61 }
62
~TestVFO__anonf726ac0b0111::TestVFO63 ~TestVFO() {
64 if (Contents) {
65 char *BufPtr;
66 unsigned BufSize;
67 clang_VirtualFileOverlay_writeToBuffer(VFO, 0, &BufPtr, &BufSize);
68 std::string BufStr(BufPtr, BufSize);
69 EXPECT_STREQ(Contents, BufStr.c_str());
70 clang_free(BufPtr);
71 }
72 clang_VirtualFileOverlay_dispose(VFO);
73 }
74 };
75 }
76
TEST(libclang,VirtualFileOverlay_Basic)77 TEST(libclang, VirtualFileOverlay_Basic) {
78 const char *contents =
79 "{\n"
80 " 'version': 0,\n"
81 " 'roots': [\n"
82 " {\n"
83 " 'type': 'directory',\n"
84 " 'name': \"/path/virtual\",\n"
85 " 'contents': [\n"
86 " {\n"
87 " 'type': 'file',\n"
88 " 'name': \"foo.h\",\n"
89 " 'external-contents': \"/real/foo.h\"\n"
90 " }\n"
91 " ]\n"
92 " }\n"
93 " ]\n"
94 "}\n";
95 TestVFO T(contents);
96 T.map("/path/virtual/foo.h", "/real/foo.h");
97 }
98
TEST(libclang,VirtualFileOverlay_Unicode)99 TEST(libclang, VirtualFileOverlay_Unicode) {
100 const char *contents =
101 "{\n"
102 " 'version': 0,\n"
103 " 'roots': [\n"
104 " {\n"
105 " 'type': 'directory',\n"
106 " 'name': \"/path/\\u266B\",\n"
107 " 'contents': [\n"
108 " {\n"
109 " 'type': 'file',\n"
110 " 'name': \"\\u2602.h\",\n"
111 " 'external-contents': \"/real/\\u2602.h\"\n"
112 " }\n"
113 " ]\n"
114 " }\n"
115 " ]\n"
116 "}\n";
117 TestVFO T(contents);
118 T.map("/path/♫/☂.h", "/real/☂.h");
119 }
120
TEST(libclang,VirtualFileOverlay_InvalidArgs)121 TEST(libclang, VirtualFileOverlay_InvalidArgs) {
122 TestVFO T(nullptr);
123 T.mapError("/path/./virtual/../foo.h", "/real/foo.h",
124 CXError_InvalidArguments);
125 }
126
TEST(libclang,VirtualFileOverlay_RemapDirectories)127 TEST(libclang, VirtualFileOverlay_RemapDirectories) {
128 const char *contents =
129 "{\n"
130 " 'version': 0,\n"
131 " 'roots': [\n"
132 " {\n"
133 " 'type': 'directory',\n"
134 " 'name': \"/another/dir\",\n"
135 " 'contents': [\n"
136 " {\n"
137 " 'type': 'file',\n"
138 " 'name': \"foo2.h\",\n"
139 " 'external-contents': \"/real/foo2.h\"\n"
140 " }\n"
141 " ]\n"
142 " },\n"
143 " {\n"
144 " 'type': 'directory',\n"
145 " 'name': \"/path/virtual/dir\",\n"
146 " 'contents': [\n"
147 " {\n"
148 " 'type': 'file',\n"
149 " 'name': \"foo1.h\",\n"
150 " 'external-contents': \"/real/foo1.h\"\n"
151 " },\n"
152 " {\n"
153 " 'type': 'file',\n"
154 " 'name': \"foo3.h\",\n"
155 " 'external-contents': \"/real/foo3.h\"\n"
156 " },\n"
157 " {\n"
158 " 'type': 'directory',\n"
159 " 'name': \"in/subdir\",\n"
160 " 'contents': [\n"
161 " {\n"
162 " 'type': 'file',\n"
163 " 'name': \"foo4.h\",\n"
164 " 'external-contents': \"/real/foo4.h\"\n"
165 " }\n"
166 " ]\n"
167 " }\n"
168 " ]\n"
169 " }\n"
170 " ]\n"
171 "}\n";
172 TestVFO T(contents);
173 T.map("/path/virtual/dir/foo1.h", "/real/foo1.h");
174 T.map("/another/dir/foo2.h", "/real/foo2.h");
175 T.map("/path/virtual/dir/foo3.h", "/real/foo3.h");
176 T.map("/path/virtual/dir/in/subdir/foo4.h", "/real/foo4.h");
177 }
178
TEST(libclang,VirtualFileOverlay_CaseInsensitive)179 TEST(libclang, VirtualFileOverlay_CaseInsensitive) {
180 const char *contents =
181 "{\n"
182 " 'version': 0,\n"
183 " 'case-sensitive': 'false',\n"
184 " 'roots': [\n"
185 " {\n"
186 " 'type': 'directory',\n"
187 " 'name': \"/path/virtual\",\n"
188 " 'contents': [\n"
189 " {\n"
190 " 'type': 'file',\n"
191 " 'name': \"foo.h\",\n"
192 " 'external-contents': \"/real/foo.h\"\n"
193 " }\n"
194 " ]\n"
195 " }\n"
196 " ]\n"
197 "}\n";
198 TestVFO T(contents);
199 T.map("/path/virtual/foo.h", "/real/foo.h");
200 clang_VirtualFileOverlay_setCaseSensitivity(T.VFO, false);
201 }
202
TEST(libclang,VirtualFileOverlay_SharedPrefix)203 TEST(libclang, VirtualFileOverlay_SharedPrefix) {
204 const char *contents =
205 "{\n"
206 " 'version': 0,\n"
207 " 'roots': [\n"
208 " {\n"
209 " 'type': 'directory',\n"
210 " 'name': \"/path/foo\",\n"
211 " 'contents': [\n"
212 " {\n"
213 " 'type': 'file',\n"
214 " 'name': \"bar\",\n"
215 " 'external-contents': \"/real/bar\"\n"
216 " },\n"
217 " {\n"
218 " 'type': 'file',\n"
219 " 'name': \"bar.h\",\n"
220 " 'external-contents': \"/real/bar.h\"\n"
221 " }\n"
222 " ]\n"
223 " },\n"
224 " {\n"
225 " 'type': 'directory',\n"
226 " 'name': \"/path/foobar\",\n"
227 " 'contents': [\n"
228 " {\n"
229 " 'type': 'file',\n"
230 " 'name': \"baz.h\",\n"
231 " 'external-contents': \"/real/baz.h\"\n"
232 " }\n"
233 " ]\n"
234 " },\n"
235 " {\n"
236 " 'type': 'directory',\n"
237 " 'name': \"/path\",\n"
238 " 'contents': [\n"
239 " {\n"
240 " 'type': 'file',\n"
241 " 'name': \"foobarbaz.h\",\n"
242 " 'external-contents': \"/real/foobarbaz.h\"\n"
243 " }\n"
244 " ]\n"
245 " }\n"
246 " ]\n"
247 "}\n";
248 TestVFO T(contents);
249 T.map("/path/foo/bar.h", "/real/bar.h");
250 T.map("/path/foo/bar", "/real/bar");
251 T.map("/path/foobar/baz.h", "/real/baz.h");
252 T.map("/path/foobarbaz.h", "/real/foobarbaz.h");
253 }
254
TEST(libclang,VirtualFileOverlay_AdjacentDirectory)255 TEST(libclang, VirtualFileOverlay_AdjacentDirectory) {
256 const char *contents =
257 "{\n"
258 " 'version': 0,\n"
259 " 'roots': [\n"
260 " {\n"
261 " 'type': 'directory',\n"
262 " 'name': \"/path/dir1\",\n"
263 " 'contents': [\n"
264 " {\n"
265 " 'type': 'file',\n"
266 " 'name': \"foo.h\",\n"
267 " 'external-contents': \"/real/foo.h\"\n"
268 " },\n"
269 " {\n"
270 " 'type': 'directory',\n"
271 " 'name': \"subdir\",\n"
272 " 'contents': [\n"
273 " {\n"
274 " 'type': 'file',\n"
275 " 'name': \"bar.h\",\n"
276 " 'external-contents': \"/real/bar.h\"\n"
277 " }\n"
278 " ]\n"
279 " }\n"
280 " ]\n"
281 " },\n"
282 " {\n"
283 " 'type': 'directory',\n"
284 " 'name': \"/path/dir2\",\n"
285 " 'contents': [\n"
286 " {\n"
287 " 'type': 'file',\n"
288 " 'name': \"baz.h\",\n"
289 " 'external-contents': \"/real/baz.h\"\n"
290 " }\n"
291 " ]\n"
292 " }\n"
293 " ]\n"
294 "}\n";
295 TestVFO T(contents);
296 T.map("/path/dir1/foo.h", "/real/foo.h");
297 T.map("/path/dir1/subdir/bar.h", "/real/bar.h");
298 T.map("/path/dir2/baz.h", "/real/baz.h");
299 }
300
TEST(libclang,VirtualFileOverlay_TopLevel)301 TEST(libclang, VirtualFileOverlay_TopLevel) {
302 const char *contents =
303 "{\n"
304 " 'version': 0,\n"
305 " 'roots': [\n"
306 " {\n"
307 " 'type': 'directory',\n"
308 " 'name': \"/\",\n"
309 " 'contents': [\n"
310 " {\n"
311 " 'type': 'file',\n"
312 " 'name': \"foo.h\",\n"
313 " 'external-contents': \"/real/foo.h\"\n"
314 " }\n"
315 " ]\n"
316 " }\n"
317 " ]\n"
318 "}\n";
319 TestVFO T(contents);
320 T.map("/foo.h", "/real/foo.h");
321 }
322
TEST(libclang,VirtualFileOverlay_Empty)323 TEST(libclang, VirtualFileOverlay_Empty) {
324 const char *contents =
325 "{\n"
326 " 'version': 0,\n"
327 " 'roots': [\n"
328 " ]\n"
329 "}\n";
330 TestVFO T(contents);
331 }
332
TEST(libclang,ModuleMapDescriptor)333 TEST(libclang, ModuleMapDescriptor) {
334 const char *Contents =
335 "framework module TestFrame {\n"
336 " umbrella header \"TestFrame.h\"\n"
337 "\n"
338 " export *\n"
339 " module * { export * }\n"
340 "}\n";
341
342 CXModuleMapDescriptor MMD = clang_ModuleMapDescriptor_create(0);
343
344 clang_ModuleMapDescriptor_setFrameworkModuleName(MMD, "TestFrame");
345 clang_ModuleMapDescriptor_setUmbrellaHeader(MMD, "TestFrame.h");
346
347 char *BufPtr;
348 unsigned BufSize;
349 clang_ModuleMapDescriptor_writeToBuffer(MMD, 0, &BufPtr, &BufSize);
350 std::string BufStr(BufPtr, BufSize);
351 EXPECT_STREQ(Contents, BufStr.c_str());
352 clang_free(BufPtr);
353 clang_ModuleMapDescriptor_dispose(MMD);
354 }
355
TEST_F(LibclangParseTest,AllSkippedRanges)356 TEST_F(LibclangParseTest, AllSkippedRanges) {
357 std::string Header = "header.h", Main = "main.cpp";
358 WriteFile(Header,
359 "#ifdef MANGOS\n"
360 "printf(\"mmm\");\n"
361 "#endif");
362 WriteFile(Main,
363 "#include \"header.h\"\n"
364 "#ifdef KIWIS\n"
365 "printf(\"mmm!!\");\n"
366 "#endif");
367
368 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
369 nullptr, 0, TUFlags);
370
371 CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
372 EXPECT_EQ(2U, Ranges->count);
373
374 CXSourceLocation cxl;
375 unsigned line;
376 cxl = clang_getRangeStart(Ranges->ranges[0]);
377 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
378 EXPECT_EQ(1U, line);
379 cxl = clang_getRangeEnd(Ranges->ranges[0]);
380 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
381 EXPECT_EQ(3U, line);
382
383 cxl = clang_getRangeStart(Ranges->ranges[1]);
384 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
385 EXPECT_EQ(2U, line);
386 cxl = clang_getRangeEnd(Ranges->ranges[1]);
387 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
388 EXPECT_EQ(4U, line);
389
390 clang_disposeSourceRangeList(Ranges);
391 }
392
TEST_F(LibclangParseTest,EvaluateChildExpression)393 TEST_F(LibclangParseTest, EvaluateChildExpression) {
394 std::string Main = "main.m";
395 WriteFile(Main, "#define kFOO @\"foo\"\n"
396 "void foobar(void) {\n"
397 " {kFOO;}\n"
398 "}\n");
399 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
400 0, TUFlags);
401
402 CXCursor C = clang_getTranslationUnitCursor(ClangTU);
403 clang_visitChildren(
404 C,
405 [](CXCursor cursor, CXCursor parent,
406 CXClientData client_data) -> CXChildVisitResult {
407 if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
408 int numberedStmt = 0;
409 clang_visitChildren(
410 cursor,
411 [](CXCursor cursor, CXCursor parent,
412 CXClientData client_data) -> CXChildVisitResult {
413 int &numberedStmt = *((int *)client_data);
414 if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt) {
415 if (numberedStmt) {
416 CXEvalResult RE = clang_Cursor_Evaluate(cursor);
417 EXPECT_NE(RE, nullptr);
418 EXPECT_EQ(clang_EvalResult_getKind(RE),
419 CXEval_ObjCStrLiteral);
420 clang_EvalResult_dispose(RE);
421 return CXChildVisit_Break;
422 }
423 numberedStmt++;
424 }
425 return CXChildVisit_Recurse;
426 },
427 &numberedStmt);
428 EXPECT_EQ(numberedStmt, 1);
429 }
430 return CXChildVisit_Continue;
431 },
432 nullptr);
433 }
434
435 class LibclangReparseTest : public LibclangParseTest {
436 public:
DisplayDiagnostics()437 void DisplayDiagnostics() {
438 unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU);
439 for (unsigned i = 0; i < NumDiagnostics; ++i) {
440 auto Diag = clang_getDiagnostic(ClangTU, i);
441 LLVM_DEBUG(llvm::dbgs()
442 << clang_getCString(clang_formatDiagnostic(
443 Diag, clang_defaultDiagnosticDisplayOptions()))
444 << "\n");
445 clang_disposeDiagnostic(Diag);
446 }
447 }
ReparseTU(unsigned num_unsaved_files,CXUnsavedFile * unsaved_files)448 bool ReparseTU(unsigned num_unsaved_files, CXUnsavedFile* unsaved_files) {
449 if (clang_reparseTranslationUnit(ClangTU, num_unsaved_files, unsaved_files,
450 clang_defaultReparseOptions(ClangTU))) {
451 LLVM_DEBUG(llvm::dbgs() << "Reparse failed\n");
452 return false;
453 }
454 DisplayDiagnostics();
455 return true;
456 }
457 };
458
TEST_F(LibclangReparseTest,FileName)459 TEST_F(LibclangReparseTest, FileName) {
460 std::string CppName = "main.cpp";
461 WriteFile(CppName, "int main() {}");
462 ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
463 nullptr, 0, TUFlags);
464 CXFile cxf = clang_getFile(ClangTU, CppName.c_str());
465
466 CXString cxname = clang_getFileName(cxf);
467 ASSERT_STREQ(clang_getCString(cxname), CppName.c_str());
468 clang_disposeString(cxname);
469
470 cxname = clang_File_tryGetRealPathName(cxf);
471 ASSERT_TRUE(llvm::StringRef(clang_getCString(cxname)).endswith("main.cpp"));
472 clang_disposeString(cxname);
473 }
474
TEST_F(LibclangReparseTest,Reparse)475 TEST_F(LibclangReparseTest, Reparse) {
476 const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
477 const char *HeaderBottom = "\n};\n#endif\n";
478 const char *CppFile = "#include \"HeaderFile.h\"\nint main() {"
479 " Foo foo; foo.bar = 7; foo.baz = 8; }\n";
480 std::string HeaderName = "HeaderFile.h";
481 std::string CppName = "CppFile.cpp";
482 WriteFile(CppName, CppFile);
483 WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
484
485 ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
486 nullptr, 0, TUFlags);
487 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
488 DisplayDiagnostics();
489
490 // Immedaitely reparse.
491 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
492 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
493
494 std::string NewHeaderContents =
495 std::string(HeaderTop) + "int baz;" + HeaderBottom;
496 WriteFile(HeaderName, NewHeaderContents);
497
498 // Reparse after fix.
499 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
500 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
501 }
502
TEST_F(LibclangReparseTest,ReparseWithModule)503 TEST_F(LibclangReparseTest, ReparseWithModule) {
504 const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
505 const char *HeaderBottom = "\n};\n#endif\n";
506 const char *MFile = "#include \"HeaderFile.h\"\nint main() {"
507 " struct Foo foo; foo.bar = 7; foo.baz = 8; }\n";
508 const char *ModFile = "module A { header \"HeaderFile.h\" }\n";
509 std::string HeaderName = "HeaderFile.h";
510 std::string MName = "MFile.m";
511 std::string ModName = "module.modulemap";
512 WriteFile(MName, MFile);
513 WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
514 WriteFile(ModName, ModFile);
515
516 std::string ModulesCache = std::string("-fmodules-cache-path=") + TestDir;
517 const char *Args[] = { "-fmodules", ModulesCache.c_str(),
518 "-I", TestDir.c_str() };
519 int NumArgs = sizeof(Args) / sizeof(Args[0]);
520 ClangTU = clang_parseTranslationUnit(Index, MName.c_str(), Args, NumArgs,
521 nullptr, 0, TUFlags);
522 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
523 DisplayDiagnostics();
524
525 // Immedaitely reparse.
526 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
527 EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
528
529 std::string NewHeaderContents =
530 std::string(HeaderTop) + "int baz;" + HeaderBottom;
531 WriteFile(HeaderName, NewHeaderContents);
532
533 // Reparse after fix.
534 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
535 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
536 }
537
TEST_F(LibclangReparseTest,clang_parseTranslationUnit2FullArgv)538 TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) {
539 // Provide a fake GCC 99.9.9 standard library that always overrides any local
540 // GCC installation.
541 std::string EmptyFiles[] = {"lib/gcc/arm-linux-gnueabi/99.9.9/crtbegin.o",
542 "include/arm-linux-gnueabi/.keep",
543 "include/c++/99.9.9/vector"};
544
545 for (auto &Name : EmptyFiles)
546 WriteFile(Name, "\n");
547
548 std::string Filename = "test.cc";
549 WriteFile(Filename, "#include <vector>\n");
550
551 std::string Clang = "bin/clang";
552 WriteFile(Clang, "");
553
554 const char *Argv[] = {Clang.c_str(), "-target", "arm-linux-gnueabi",
555 "-stdlib=libstdc++", "--gcc-toolchain="};
556
557 EXPECT_EQ(CXError_Success,
558 clang_parseTranslationUnit2FullArgv(Index, Filename.c_str(), Argv,
559 sizeof(Argv) / sizeof(Argv[0]),
560 nullptr, 0, TUFlags, &ClangTU));
561 EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
562 DisplayDiagnostics();
563 }
564
565 class LibclangPrintingPolicyTest : public LibclangParseTest {
566 public:
567 CXPrintingPolicy Policy = nullptr;
568
SetUp()569 void SetUp() override {
570 LibclangParseTest::SetUp();
571 std::string File = "file.cpp";
572 WriteFile(File, "int i;\n");
573 ClangTU = clang_parseTranslationUnit(Index, File.c_str(), nullptr, 0,
574 nullptr, 0, TUFlags);
575 CXCursor TUCursor = clang_getTranslationUnitCursor(ClangTU);
576 Policy = clang_getCursorPrintingPolicy(TUCursor);
577 }
TearDown()578 void TearDown() override {
579 clang_PrintingPolicy_dispose(Policy);
580 LibclangParseTest::TearDown();
581 }
582 };
583
TEST_F(LibclangPrintingPolicyTest,SetAndGetProperties)584 TEST_F(LibclangPrintingPolicyTest, SetAndGetProperties) {
585 for (unsigned Value = 0; Value < 2; ++Value) {
586 for (int I = 0; I < CXPrintingPolicy_LastProperty; ++I) {
587 auto Property = static_cast<enum CXPrintingPolicyProperty>(I);
588
589 clang_PrintingPolicy_setProperty(Policy, Property, Value);
590 EXPECT_EQ(Value, clang_PrintingPolicy_getProperty(Policy, Property));
591 }
592 }
593 }
594
TEST_F(LibclangReparseTest,PreprocessorSkippedRanges)595 TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) {
596 std::string Header = "header.h", Main = "main.cpp";
597 WriteFile(Header,
598 "#ifdef MANGOS\n"
599 "printf(\"mmm\");\n"
600 "#endif");
601 WriteFile(Main,
602 "#include \"header.h\"\n"
603 "#ifdef GUAVA\n"
604 "#endif\n"
605 "#ifdef KIWIS\n"
606 "printf(\"mmm!!\");\n"
607 "#endif");
608
609 for (int i = 0; i != 3; ++i) {
610 unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble;
611 if (i == 2)
612 flags |= CXTranslationUnit_CreatePreambleOnFirstParse;
613
614 if (i != 0)
615 clang_disposeTranslationUnit(ClangTU); // dispose from previous iter
616
617 // parse once
618 ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
619 nullptr, 0, flags);
620 if (i != 0) {
621 // reparse
622 ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
623 }
624
625 // Check all ranges are there
626 CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
627 EXPECT_EQ(3U, Ranges->count);
628
629 CXSourceLocation cxl;
630 unsigned line;
631 cxl = clang_getRangeStart(Ranges->ranges[0]);
632 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
633 EXPECT_EQ(1U, line);
634 cxl = clang_getRangeEnd(Ranges->ranges[0]);
635 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
636 EXPECT_EQ(3U, line);
637
638 cxl = clang_getRangeStart(Ranges->ranges[1]);
639 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
640 EXPECT_EQ(2U, line);
641 cxl = clang_getRangeEnd(Ranges->ranges[1]);
642 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
643 EXPECT_EQ(3U, line);
644
645 cxl = clang_getRangeStart(Ranges->ranges[2]);
646 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
647 EXPECT_EQ(4U, line);
648 cxl = clang_getRangeEnd(Ranges->ranges[2]);
649 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
650 EXPECT_EQ(6U, line);
651
652 clang_disposeSourceRangeList(Ranges);
653
654 // Check obtaining ranges by each file works
655 CXFile cxf = clang_getFile(ClangTU, Header.c_str());
656 Ranges = clang_getSkippedRanges(ClangTU, cxf);
657 EXPECT_EQ(1U, Ranges->count);
658 cxl = clang_getRangeStart(Ranges->ranges[0]);
659 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
660 EXPECT_EQ(1U, line);
661 clang_disposeSourceRangeList(Ranges);
662
663 cxf = clang_getFile(ClangTU, Main.c_str());
664 Ranges = clang_getSkippedRanges(ClangTU, cxf);
665 EXPECT_EQ(2U, Ranges->count);
666 cxl = clang_getRangeStart(Ranges->ranges[0]);
667 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
668 EXPECT_EQ(2U, line);
669 cxl = clang_getRangeStart(Ranges->ranges[1]);
670 clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
671 EXPECT_EQ(4U, line);
672 clang_disposeSourceRangeList(Ranges);
673 }
674 }
675
676 class LibclangSerializationTest : public LibclangParseTest {
677 public:
SaveAndLoadTU(const std::string & Filename)678 bool SaveAndLoadTU(const std::string &Filename) {
679 unsigned options = clang_defaultSaveOptions(ClangTU);
680 if (clang_saveTranslationUnit(ClangTU, Filename.c_str(), options) !=
681 CXSaveError_None) {
682 LLVM_DEBUG(llvm::dbgs() << "Saving failed\n");
683 return false;
684 }
685
686 clang_disposeTranslationUnit(ClangTU);
687
688 ClangTU = clang_createTranslationUnit(Index, Filename.c_str());
689
690 if (!ClangTU) {
691 LLVM_DEBUG(llvm::dbgs() << "Loading failed\n");
692 return false;
693 }
694
695 return true;
696 }
697 };
698
TEST_F(LibclangSerializationTest,TokenKindsAreCorrectAfterLoading)699 TEST_F(LibclangSerializationTest, TokenKindsAreCorrectAfterLoading) {
700 // Ensure that "class" is recognized as a keyword token after serializing
701 // and reloading the AST, as it is not a keyword for the default LangOptions.
702 std::string HeaderName = "test.h";
703 WriteFile(HeaderName, "enum class Something {};");
704
705 const char *Argv[] = {"-xc++-header", "-std=c++11"};
706
707 ClangTU = clang_parseTranslationUnit(Index, HeaderName.c_str(), Argv,
708 sizeof(Argv) / sizeof(Argv[0]), nullptr,
709 0, TUFlags);
710
711 auto CheckTokenKinds = [=]() {
712 CXSourceRange Range =
713 clang_getCursorExtent(clang_getTranslationUnitCursor(ClangTU));
714
715 CXToken *Tokens;
716 unsigned int NumTokens;
717 clang_tokenize(ClangTU, Range, &Tokens, &NumTokens);
718
719 ASSERT_EQ(6u, NumTokens);
720 EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[0]));
721 EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[1]));
722 EXPECT_EQ(CXToken_Identifier, clang_getTokenKind(Tokens[2]));
723 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[3]));
724 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[4]));
725 EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[5]));
726
727 clang_disposeTokens(ClangTU, Tokens, NumTokens);
728 };
729
730 CheckTokenKinds();
731
732 std::string ASTName = "test.ast";
733 WriteFile(ASTName, "");
734
735 ASSERT_TRUE(SaveAndLoadTU(ASTName));
736
737 CheckTokenKinds();
738 }
739