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