llvm-project
1412 строк · 46.0 Кб
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
27TEST(libclang, clang_parseTranslationUnit2_InvalidArgs) {
28EXPECT_EQ(CXError_InvalidArguments,
29clang_parseTranslationUnit2(nullptr, nullptr, nullptr, 0, nullptr,
300, 0, nullptr));
31}
32
33TEST(libclang, clang_createTranslationUnit_InvalidArgs) {
34EXPECT_EQ(nullptr, clang_createTranslationUnit(nullptr, nullptr));
35}
36
37TEST(libclang, clang_createTranslationUnit2_InvalidArgs) {
38EXPECT_EQ(CXError_InvalidArguments,
39clang_createTranslationUnit2(nullptr, nullptr, nullptr));
40
41CXTranslationUnit TU = reinterpret_cast<CXTranslationUnit>(1);
42EXPECT_EQ(CXError_InvalidArguments,
43clang_createTranslationUnit2(nullptr, nullptr, &TU));
44EXPECT_EQ(nullptr, TU);
45}
46
47namespace {
48struct TestVFO {
49const char *Contents;
50CXVirtualFileOverlay VFO;
51
52TestVFO(const char *Contents) : Contents(Contents) {
53VFO = clang_VirtualFileOverlay_create(0);
54}
55
56void map(const char *VPath, const char *RPath) {
57CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
58EXPECT_EQ(Err, CXError_Success);
59}
60
61void mapError(const char *VPath, const char *RPath, CXErrorCode ExpErr) {
62CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
63EXPECT_EQ(Err, ExpErr);
64}
65
66~TestVFO() {
67if (Contents) {
68char *BufPtr;
69unsigned BufSize;
70clang_VirtualFileOverlay_writeToBuffer(VFO, 0, &BufPtr, &BufSize);
71std::string BufStr(BufPtr, BufSize);
72EXPECT_STREQ(Contents, BufStr.c_str());
73clang_free(BufPtr);
74}
75clang_VirtualFileOverlay_dispose(VFO);
76}
77};
78}
79
80TEST(libclang, VirtualFileOverlay_Basic) {
81const 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";
98TestVFO T(contents);
99T.map("/path/virtual/foo.h", "/real/foo.h");
100}
101
102TEST(libclang, VirtualFileOverlay_Unicode) {
103const 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";
120TestVFO T(contents);
121T.map("/path/♫/☂.h", "/real/☂.h");
122}
123
124TEST(libclang, VirtualFileOverlay_InvalidArgs) {
125TestVFO T(nullptr);
126T.mapError("/path/./virtual/../foo.h", "/real/foo.h",
127CXError_InvalidArguments);
128}
129
130TEST(libclang, VirtualFileOverlay_RemapDirectories) {
131const 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";
175TestVFO T(contents);
176T.map("/path/virtual/dir/foo1.h", "/real/foo1.h");
177T.map("/another/dir/foo2.h", "/real/foo2.h");
178T.map("/path/virtual/dir/foo3.h", "/real/foo3.h");
179T.map("/path/virtual/dir/in/subdir/foo4.h", "/real/foo4.h");
180}
181
182TEST(libclang, VirtualFileOverlay_CaseInsensitive) {
183const 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";
201TestVFO T(contents);
202T.map("/path/virtual/foo.h", "/real/foo.h");
203clang_VirtualFileOverlay_setCaseSensitivity(T.VFO, false);
204}
205
206TEST(libclang, VirtualFileOverlay_SharedPrefix) {
207const 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";
251TestVFO T(contents);
252T.map("/path/foo/bar.h", "/real/bar.h");
253T.map("/path/foo/bar", "/real/bar");
254T.map("/path/foobar/baz.h", "/real/baz.h");
255T.map("/path/foobarbaz.h", "/real/foobarbaz.h");
256}
257
258TEST(libclang, VirtualFileOverlay_AdjacentDirectory) {
259const 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";
298TestVFO T(contents);
299T.map("/path/dir1/foo.h", "/real/foo.h");
300T.map("/path/dir1/subdir/bar.h", "/real/bar.h");
301T.map("/path/dir2/baz.h", "/real/baz.h");
302}
303
304TEST(libclang, VirtualFileOverlay_TopLevel) {
305const 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";
322TestVFO T(contents);
323T.map("/foo.h", "/real/foo.h");
324}
325
326TEST(libclang, VirtualFileOverlay_Empty) {
327const char *contents =
328"{\n"
329" 'version': 0,\n"
330" 'roots': [\n"
331" ]\n"
332"}\n";
333TestVFO T(contents);
334}
335
336TEST(libclang, ModuleMapDescriptor) {
337const 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
345CXModuleMapDescriptor MMD = clang_ModuleMapDescriptor_create(0);
346
347clang_ModuleMapDescriptor_setFrameworkModuleName(MMD, "TestFrame");
348clang_ModuleMapDescriptor_setUmbrellaHeader(MMD, "TestFrame.h");
349
350char *BufPtr;
351unsigned BufSize;
352clang_ModuleMapDescriptor_writeToBuffer(MMD, 0, &BufPtr, &BufSize);
353std::string BufStr(BufPtr, BufSize);
354EXPECT_STREQ(Contents, BufStr.c_str());
355clang_free(BufPtr);
356clang_ModuleMapDescriptor_dispose(MMD);
357}
358
359TEST_F(LibclangParseTest, GlobalOptions) {
360EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None);
361}
362
363class LibclangIndexOptionsTest : public LibclangParseTest {
364virtual void AdjustOptions(CXIndexOptions &Opts) {}
365
366protected:
367void CreateIndex() override {
368CXIndexOptions Opts;
369memset(&Opts, 0, sizeof(Opts));
370Opts.Size = sizeof(CXIndexOptions);
371AdjustOptions(Opts);
372Index = clang_createIndexWithOptions(&Opts);
373ASSERT_TRUE(Index);
374}
375};
376
377TEST_F(LibclangIndexOptionsTest, GlobalOptions) {
378EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None);
379}
380
381class LibclangIndexingEnabledIndexOptionsTest
382: public LibclangIndexOptionsTest {
383void AdjustOptions(CXIndexOptions &Opts) override {
384Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled;
385}
386};
387
388TEST_F(LibclangIndexingEnabledIndexOptionsTest, GlobalOptions) {
389EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
390CXGlobalOpt_ThreadBackgroundPriorityForIndexing);
391}
392
393class LibclangIndexingDisabledEditingEnabledIndexOptionsTest
394: public LibclangIndexOptionsTest {
395void AdjustOptions(CXIndexOptions &Opts) override {
396Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Disabled;
397Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled;
398}
399};
400
401TEST_F(LibclangIndexingDisabledEditingEnabledIndexOptionsTest, GlobalOptions) {
402EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
403CXGlobalOpt_ThreadBackgroundPriorityForEditing);
404}
405
406class LibclangBothEnabledIndexOptionsTest : public LibclangIndexOptionsTest {
407void AdjustOptions(CXIndexOptions &Opts) override {
408Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled;
409Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled;
410}
411};
412
413TEST_F(LibclangBothEnabledIndexOptionsTest, GlobalOptions) {
414EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
415CXGlobalOpt_ThreadBackgroundPriorityForAll);
416}
417
418class LibclangPreambleStorageTest : public LibclangParseTest {
419std::string Main = "main.cpp";
420
421protected:
422std::string PreambleDir;
423void InitializePreambleDir() {
424llvm::SmallString<128> PathBuffer(TestDir);
425llvm::sys::path::append(PathBuffer, "preambles");
426namespace fs = llvm::sys::fs;
427ASSERT_FALSE(fs::create_directory(PathBuffer, false, fs::perms::owner_all));
428
429PreambleDir = static_cast<std::string>(PathBuffer);
430FilesAndDirsToRemove.insert(PreambleDir);
431}
432
433public:
434void CountPreamblesInPreambleDir(int PreambleCount) {
435// For some reason, the preamble is not created without '\n' before `int`.
436WriteFile(Main, "\nint main() {}");
437
438TUFlags |= CXTranslationUnit_CreatePreambleOnFirstParse;
439ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
440nullptr, 0, TUFlags);
441
442int FileCount = 0;
443
444namespace fs = llvm::sys::fs;
445std::error_code EC;
446for (fs::directory_iterator File(PreambleDir, EC), FileEnd;
447File != FileEnd && !EC; File.increment(EC)) {
448++FileCount;
449
450EXPECT_EQ(File->type(), fs::file_type::regular_file);
451
452const auto Filename = llvm::sys::path::filename(File->path());
453EXPECT_EQ(Filename.size(), std::strlen("preamble-%%%%%%.pch"));
454EXPECT_TRUE(Filename.starts_with("preamble-"));
455EXPECT_TRUE(Filename.ends_with(".pch"));
456
457const auto Status = File->status();
458ASSERT_TRUE(Status);
459if (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.
464EXPECT_EQ(Status->permissions(), fs::owner_read | fs::owner_write);
465}
466}
467
468EXPECT_EQ(FileCount, PreambleCount);
469}
470};
471
472class LibclangNotOverriddenPreambleStoragePathTest
473: public LibclangPreambleStorageTest {
474protected:
475void CreateIndex() override {
476InitializePreambleDir();
477LibclangPreambleStorageTest::CreateIndex();
478}
479};
480
481class LibclangSetPreambleStoragePathTest : public LibclangPreambleStorageTest {
482virtual bool StorePreamblesInMemory() { return false; }
483virtual const char *PreambleStoragePath() = 0;
484
485protected:
486void CreateIndex() override {
487InitializePreambleDir();
488
489CXIndexOptions Opts{};
490Opts.Size = sizeof(CXIndexOptions);
491Opts.StorePreamblesInMemory = StorePreamblesInMemory();
492Opts.PreambleStoragePath = PreambleStoragePath();
493Index = clang_createIndexWithOptions(&Opts);
494ASSERT_TRUE(Index);
495}
496};
497
498class LibclangNullPreambleStoragePathTest
499: public LibclangSetPreambleStoragePathTest {
500const char *PreambleStoragePath() override { return nullptr; }
501};
502class LibclangEmptyPreambleStoragePathTest
503: public LibclangSetPreambleStoragePathTest {
504const char *PreambleStoragePath() override { return ""; }
505};
506class LibclangPreambleDirPreambleStoragePathTest
507: public LibclangSetPreambleStoragePathTest {
508const char *PreambleStoragePath() override { return PreambleDir.c_str(); }
509};
510
511class LibclangStoreInMemoryNullPreambleStoragePathTest
512: public LibclangNullPreambleStoragePathTest {
513bool StorePreamblesInMemory() override { return true; }
514};
515class LibclangStoreInMemoryEmptyPreambleStoragePathTest
516: public LibclangEmptyPreambleStoragePathTest {
517bool StorePreamblesInMemory() override { return true; }
518};
519class LibclangStoreInMemoryPreambleDirPreambleStoragePathTest
520: public LibclangPreambleDirPreambleStoragePathTest {
521bool StorePreamblesInMemory() override { return true; }
522};
523
524TEST_F(LibclangNotOverriddenPreambleStoragePathTest, CountPreambles) {
525CountPreamblesInPreambleDir(0);
526}
527TEST_F(LibclangNullPreambleStoragePathTest, CountPreambles) {
528CountPreamblesInPreambleDir(0);
529}
530TEST_F(LibclangEmptyPreambleStoragePathTest, CountPreambles) {
531CountPreamblesInPreambleDir(0);
532}
533TEST_F(LibclangPreambleDirPreambleStoragePathTest, CountPreambles) {
534CountPreamblesInPreambleDir(1);
535}
536TEST_F(LibclangStoreInMemoryNullPreambleStoragePathTest, CountPreambles) {
537CountPreamblesInPreambleDir(0);
538}
539TEST_F(LibclangStoreInMemoryEmptyPreambleStoragePathTest, CountPreambles) {
540CountPreamblesInPreambleDir(0);
541}
542TEST_F(LibclangStoreInMemoryPreambleDirPreambleStoragePathTest,
543CountPreambles) {
544CountPreamblesInPreambleDir(0);
545}
546
547TEST_F(LibclangParseTest, AllSkippedRanges) {
548std::string Header = "header.h", Main = "main.cpp";
549WriteFile(Header,
550"#ifdef MANGOS\n"
551"printf(\"mmm\");\n"
552"#endif");
553WriteFile(Main,
554"#include \"header.h\"\n"
555"#ifdef KIWIS\n"
556"printf(\"mmm!!\");\n"
557"#endif");
558
559ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
560nullptr, 0, TUFlags);
561
562CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
563EXPECT_EQ(2U, Ranges->count);
564
565CXSourceLocation cxl;
566unsigned line;
567cxl = clang_getRangeStart(Ranges->ranges[0]);
568clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
569EXPECT_EQ(1U, line);
570cxl = clang_getRangeEnd(Ranges->ranges[0]);
571clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
572EXPECT_EQ(3U, line);
573
574cxl = clang_getRangeStart(Ranges->ranges[1]);
575clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
576EXPECT_EQ(2U, line);
577cxl = clang_getRangeEnd(Ranges->ranges[1]);
578clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
579EXPECT_EQ(4U, line);
580
581clang_disposeSourceRangeList(Ranges);
582}
583
584TEST_F(LibclangParseTest, EvaluateChildExpression) {
585std::string Main = "main.m";
586WriteFile(Main, "#define kFOO @\"foo\"\n"
587"void foobar(void) {\n"
588" {kFOO;}\n"
589"}\n");
590ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
5910, TUFlags);
592
593CXCursor C = clang_getTranslationUnitCursor(ClangTU);
594clang_visitChildren(
595C,
596[](CXCursor cursor, CXCursor parent,
597CXClientData client_data) -> CXChildVisitResult {
598if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
599int numberedStmt = 0;
600clang_visitChildren(
601cursor,
602[](CXCursor cursor, CXCursor parent,
603CXClientData client_data) -> CXChildVisitResult {
604int &numberedStmt = *((int *)client_data);
605if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt) {
606if (numberedStmt) {
607CXEvalResult RE = clang_Cursor_Evaluate(cursor);
608EXPECT_NE(RE, nullptr);
609EXPECT_EQ(clang_EvalResult_getKind(RE),
610CXEval_ObjCStrLiteral);
611clang_EvalResult_dispose(RE);
612return CXChildVisit_Break;
613}
614numberedStmt++;
615}
616return CXChildVisit_Recurse;
617},
618&numberedStmt);
619EXPECT_EQ(numberedStmt, 1);
620}
621return CXChildVisit_Continue;
622},
623nullptr);
624}
625
626class LibclangReparseTest : public LibclangParseTest {
627public:
628void DisplayDiagnostics() {
629unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU);
630for (unsigned i = 0; i < NumDiagnostics; ++i) {
631auto Diag = clang_getDiagnostic(ClangTU, i);
632LLVM_DEBUG(llvm::dbgs()
633<< clang_getCString(clang_formatDiagnostic(
634Diag, clang_defaultDiagnosticDisplayOptions()))
635<< "\n");
636clang_disposeDiagnostic(Diag);
637}
638}
639bool ReparseTU(unsigned num_unsaved_files, CXUnsavedFile* unsaved_files) {
640if (clang_reparseTranslationUnit(ClangTU, num_unsaved_files, unsaved_files,
641clang_defaultReparseOptions(ClangTU))) {
642LLVM_DEBUG(llvm::dbgs() << "Reparse failed\n");
643return false;
644}
645DisplayDiagnostics();
646return true;
647}
648};
649
650TEST_F(LibclangReparseTest, FileName) {
651std::string CppName = "main.cpp";
652WriteFile(CppName, "int main() {}");
653ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
654nullptr, 0, TUFlags);
655CXFile cxf = clang_getFile(ClangTU, CppName.c_str());
656
657CXString cxname = clang_getFileName(cxf);
658ASSERT_STREQ(clang_getCString(cxname), CppName.c_str());
659clang_disposeString(cxname);
660
661cxname = clang_File_tryGetRealPathName(cxf);
662ASSERT_TRUE(llvm::StringRef(clang_getCString(cxname)).ends_with("main.cpp"));
663clang_disposeString(cxname);
664}
665
666TEST_F(LibclangReparseTest, Reparse) {
667const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
668const char *HeaderBottom = "\n};\n#endif\n";
669const char *CppFile = "#include \"HeaderFile.h\"\nint main() {"
670" Foo foo; foo.bar = 7; foo.baz = 8; }\n";
671std::string HeaderName = "HeaderFile.h";
672std::string CppName = "CppFile.cpp";
673WriteFile(CppName, CppFile);
674WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
675
676ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
677nullptr, 0, TUFlags);
678EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
679DisplayDiagnostics();
680
681// Immedaitely reparse.
682ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
683EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
684
685std::string NewHeaderContents =
686std::string(HeaderTop) + "int baz;" + HeaderBottom;
687WriteFile(HeaderName, NewHeaderContents);
688
689// Reparse after fix.
690ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
691EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
692}
693
694TEST_F(LibclangReparseTest, ReparseWithModule) {
695const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
696const char *HeaderBottom = "\n};\n#endif\n";
697const char *MFile = "#include \"HeaderFile.h\"\nint main() {"
698" struct Foo foo; foo.bar = 7; foo.baz = 8; }\n";
699const char *ModFile = "module A { header \"HeaderFile.h\" }\n";
700std::string HeaderName = "HeaderFile.h";
701std::string MName = "MFile.m";
702std::string ModName = "module.modulemap";
703WriteFile(MName, MFile);
704WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
705WriteFile(ModName, ModFile);
706
707// Removing recursively is necessary to delete the module cache.
708RemoveTestDirRecursivelyDuringTeardown = true;
709std::string ModulesCache = std::string("-fmodules-cache-path=") + TestDir;
710const char *Args[] = { "-fmodules", ModulesCache.c_str(),
711"-I", TestDir.c_str() };
712int NumArgs = std::size(Args);
713ClangTU = clang_parseTranslationUnit(Index, MName.c_str(), Args, NumArgs,
714nullptr, 0, TUFlags);
715EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
716DisplayDiagnostics();
717
718// Immedaitely reparse.
719ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
720EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
721
722std::string NewHeaderContents =
723std::string(HeaderTop) + "int baz;" + HeaderBottom;
724WriteFile(HeaderName, NewHeaderContents);
725
726// Reparse after fix.
727ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
728EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
729}
730
731TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) {
732// Provide a fake GCC 99.9.9 standard library that always overrides any local
733// GCC installation.
734std::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
738for (auto &Name : EmptyFiles)
739WriteFile(Name, "\n");
740
741std::string Filename = "test.cc";
742WriteFile(Filename, "#include <vector>\n");
743
744std::string Clang = "bin/clang";
745WriteFile(Clang, "");
746
747const char *Argv[] = {Clang.c_str(), "-target", "arm-linux-gnueabi",
748"-stdlib=libstdc++", "--gcc-toolchain="};
749
750EXPECT_EQ(CXError_Success,
751clang_parseTranslationUnit2FullArgv(Index, Filename.c_str(), Argv,
752std::size(Argv),
753nullptr, 0, TUFlags, &ClangTU));
754EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
755DisplayDiagnostics();
756}
757
758class LibclangPrintingPolicyTest : public LibclangParseTest {
759public:
760CXPrintingPolicy Policy = nullptr;
761
762void SetUp() override {
763LibclangParseTest::SetUp();
764std::string File = "file.cpp";
765WriteFile(File, "int i;\n");
766ClangTU = clang_parseTranslationUnit(Index, File.c_str(), nullptr, 0,
767nullptr, 0, TUFlags);
768CXCursor TUCursor = clang_getTranslationUnitCursor(ClangTU);
769Policy = clang_getCursorPrintingPolicy(TUCursor);
770}
771void TearDown() override {
772clang_PrintingPolicy_dispose(Policy);
773LibclangParseTest::TearDown();
774}
775};
776
777TEST_F(LibclangPrintingPolicyTest, SetAndGetProperties) {
778for (unsigned Value = 0; Value < 2; ++Value) {
779for (int I = 0; I < CXPrintingPolicy_LastProperty; ++I) {
780auto Property = static_cast<enum CXPrintingPolicyProperty>(I);
781
782clang_PrintingPolicy_setProperty(Policy, Property, Value);
783EXPECT_EQ(Value, clang_PrintingPolicy_getProperty(Policy, Property));
784}
785}
786}
787
788TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) {
789std::string Header = "header.h", Main = "main.cpp";
790WriteFile(Header,
791"#ifdef MANGOS\n"
792"printf(\"mmm\");\n"
793"#endif");
794WriteFile(Main,
795"#include \"header.h\"\n"
796"#ifdef GUAVA\n"
797"#endif\n"
798"#ifdef KIWIS\n"
799"printf(\"mmm!!\");\n"
800"#endif");
801
802for (int i = 0; i != 3; ++i) {
803unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble;
804if (i == 2)
805flags |= CXTranslationUnit_CreatePreambleOnFirstParse;
806
807if (i != 0)
808clang_disposeTranslationUnit(ClangTU); // dispose from previous iter
809
810// parse once
811ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
812nullptr, 0, flags);
813if (i != 0) {
814// reparse
815ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
816}
817
818// Check all ranges are there
819CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
820EXPECT_EQ(3U, Ranges->count);
821
822CXSourceLocation cxl;
823unsigned line;
824cxl = clang_getRangeStart(Ranges->ranges[0]);
825clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
826EXPECT_EQ(1U, line);
827cxl = clang_getRangeEnd(Ranges->ranges[0]);
828clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
829EXPECT_EQ(3U, line);
830
831cxl = clang_getRangeStart(Ranges->ranges[1]);
832clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
833EXPECT_EQ(2U, line);
834cxl = clang_getRangeEnd(Ranges->ranges[1]);
835clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
836EXPECT_EQ(3U, line);
837
838cxl = clang_getRangeStart(Ranges->ranges[2]);
839clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
840EXPECT_EQ(4U, line);
841cxl = clang_getRangeEnd(Ranges->ranges[2]);
842clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
843EXPECT_EQ(6U, line);
844
845clang_disposeSourceRangeList(Ranges);
846
847// Check obtaining ranges by each file works
848CXFile cxf = clang_getFile(ClangTU, Header.c_str());
849Ranges = clang_getSkippedRanges(ClangTU, cxf);
850EXPECT_EQ(1U, Ranges->count);
851cxl = clang_getRangeStart(Ranges->ranges[0]);
852clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
853EXPECT_EQ(1U, line);
854clang_disposeSourceRangeList(Ranges);
855
856cxf = clang_getFile(ClangTU, Main.c_str());
857Ranges = clang_getSkippedRanges(ClangTU, cxf);
858EXPECT_EQ(2U, Ranges->count);
859cxl = clang_getRangeStart(Ranges->ranges[0]);
860clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
861EXPECT_EQ(2U, line);
862cxl = clang_getRangeStart(Ranges->ranges[1]);
863clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
864EXPECT_EQ(4U, line);
865clang_disposeSourceRangeList(Ranges);
866}
867}
868
869class LibclangSerializationTest : public LibclangParseTest {
870public:
871bool SaveAndLoadTU(const std::string &Filename) {
872unsigned options = clang_defaultSaveOptions(ClangTU);
873if (clang_saveTranslationUnit(ClangTU, Filename.c_str(), options) !=
874CXSaveError_None) {
875LLVM_DEBUG(llvm::dbgs() << "Saving failed\n");
876return false;
877}
878
879clang_disposeTranslationUnit(ClangTU);
880
881ClangTU = clang_createTranslationUnit(Index, Filename.c_str());
882
883if (!ClangTU) {
884LLVM_DEBUG(llvm::dbgs() << "Loading failed\n");
885return false;
886}
887
888return true;
889}
890};
891
892TEST_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.
895std::string HeaderName = "test.h";
896WriteFile(HeaderName, "enum class Something {};");
897
898const char *Argv[] = {"-xc++-header", "-std=c++11"};
899
900ClangTU = clang_parseTranslationUnit(Index, HeaderName.c_str(), Argv,
901std::size(Argv), nullptr,
9020, TUFlags);
903
904auto CheckTokenKinds = [=]() {
905CXSourceRange Range =
906clang_getCursorExtent(clang_getTranslationUnitCursor(ClangTU));
907
908CXToken *Tokens;
909unsigned int NumTokens;
910clang_tokenize(ClangTU, Range, &Tokens, &NumTokens);
911
912ASSERT_EQ(6u, NumTokens);
913EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[0]));
914EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[1]));
915EXPECT_EQ(CXToken_Identifier, clang_getTokenKind(Tokens[2]));
916EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[3]));
917EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[4]));
918EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[5]));
919
920clang_disposeTokens(ClangTU, Tokens, NumTokens);
921};
922
923CheckTokenKinds();
924
925std::string ASTName = "test.ast";
926WriteFile(ASTName, "");
927
928ASSERT_TRUE(SaveAndLoadTU(ASTName));
929
930CheckTokenKinds();
931}
932
933TEST_F(LibclangParseTest, clang_getVarDeclInitializer) {
934std::string Main = "main.cpp";
935WriteFile(Main, "int foo() { return 5; }; const int a = foo();");
936ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
9370, TUFlags);
938
939CXCursor C = clang_getTranslationUnitCursor(ClangTU);
940clang_visitChildren(
941C,
942[](CXCursor cursor, CXCursor parent,
943CXClientData client_data) -> CXChildVisitResult {
944if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
945const CXCursor Initializer = clang_Cursor_getVarDeclInitializer(cursor);
946EXPECT_FALSE(clang_Cursor_isNull(Initializer));
947CXString Spelling = clang_getCursorSpelling(Initializer);
948const char* const SpellingCSstr = clang_getCString(Spelling);
949EXPECT_TRUE(SpellingCSstr);
950EXPECT_EQ(std::string(SpellingCSstr), std::string("foo"));
951clang_disposeString(Spelling);
952return CXChildVisit_Break;
953}
954return CXChildVisit_Continue;
955},
956nullptr);
957}
958
959TEST_F(LibclangParseTest, clang_hasVarDeclGlobalStorageFalse) {
960std::string Main = "main.cpp";
961WriteFile(Main, "void foo() { int a; }");
962ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
9630, TUFlags);
964
965CXCursor C = clang_getTranslationUnitCursor(ClangTU);
966clang_visitChildren(
967C,
968[](CXCursor cursor, CXCursor parent,
969CXClientData client_data) -> CXChildVisitResult {
970if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
971EXPECT_FALSE(clang_Cursor_hasVarDeclGlobalStorage(cursor));
972return CXChildVisit_Break;
973}
974return CXChildVisit_Continue;
975},
976nullptr);
977}
978
979TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclGlobalStorageTrue) {
980std::string Main = "main.cpp";
981WriteFile(Main, "int a;");
982ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
9830, TUFlags);
984
985CXCursor C = clang_getTranslationUnitCursor(ClangTU);
986clang_visitChildren(
987C,
988[](CXCursor cursor, CXCursor parent,
989CXClientData client_data) -> CXChildVisitResult {
990if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
991EXPECT_TRUE(clang_Cursor_hasVarDeclGlobalStorage(cursor));
992return CXChildVisit_Break;
993}
994return CXChildVisit_Continue;
995},
996nullptr);
997}
998
999TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageFalse) {
1000std::string Main = "main.cpp";
1001WriteFile(Main, "int a;");
1002ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
10030, TUFlags);
1004
1005CXCursor C = clang_getTranslationUnitCursor(ClangTU);
1006clang_visitChildren(
1007C,
1008[](CXCursor cursor, CXCursor parent,
1009CXClientData client_data) -> CXChildVisitResult {
1010if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
1011EXPECT_FALSE(clang_Cursor_hasVarDeclExternalStorage(cursor));
1012return CXChildVisit_Break;
1013}
1014return CXChildVisit_Continue;
1015},
1016nullptr);
1017}
1018
1019TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageTrue) {
1020std::string Main = "main.cpp";
1021WriteFile(Main, "extern int a;");
1022ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
10230, TUFlags);
1024
1025CXCursor C = clang_getTranslationUnitCursor(ClangTU);
1026clang_visitChildren(
1027C,
1028[](CXCursor cursor, CXCursor parent,
1029CXClientData client_data) -> CXChildVisitResult {
1030if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
1031EXPECT_TRUE(clang_Cursor_hasVarDeclExternalStorage(cursor));
1032return CXChildVisit_Break;
1033}
1034return CXChildVisit_Continue;
1035},
1036nullptr);
1037}
1038
1039TEST_F(LibclangParseTest, clang_getUnqualifiedTypeRemovesQualifiers) {
1040std::string Header = "header.h";
1041WriteFile(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
1052auto is_qualified = [](CXType type) -> bool {
1053return clang_isConstQualifiedType(type) ||
1054clang_isVolatileQualifiedType(type) ||
1055clang_isRestrictQualifiedType(type);
1056};
1057
1058ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), nullptr, 0,
1059nullptr, 0, TUFlags);
1060
1061Traverse([&is_qualified](CXCursor cursor, CXCursor) {
1062if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
1063CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0);
1064EXPECT_TRUE(is_qualified(arg_type))
1065<< "Input data '" << fromCXString(clang_getCursorSpelling(cursor))
1066<< "' first argument does not have a qualified type.";
1067
1068CXType unqualified_arg_type = clang_getUnqualifiedType(arg_type);
1069EXPECT_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
1074return CXChildVisit_Continue;
1075});
1076}
1077
1078TEST_F(LibclangParseTest, clang_getNonReferenceTypeRemovesRefQualifiers) {
1079std::string Header = "header.h";
1080WriteFile(Header, "void foo1(int&);\n"
1081"void foo2(int&&);\n");
1082
1083auto is_ref_qualified = [](CXType type) -> bool {
1084return (type.kind == CXType_LValueReference) ||
1085(type.kind == CXType_RValueReference);
1086};
1087
1088const char *Args[] = {"-xc++"};
1089ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), Args, 1, nullptr,
10900, TUFlags);
1091
1092Traverse([&is_ref_qualified](CXCursor cursor, CXCursor) {
1093if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
1094CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0);
1095EXPECT_TRUE(is_ref_qualified(arg_type))
1096<< "Input data '" << fromCXString(clang_getCursorSpelling(cursor))
1097<< "' first argument does not have a ref-qualified type.";
1098
1099CXType non_reference_arg_type = clang_getNonReferenceType(arg_type);
1100EXPECT_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
1106return CXChildVisit_Continue;
1107});
1108}
1109
1110TEST_F(LibclangParseTest, VisitUsingTypeLoc) {
1111const char testSource[] = R"cpp(
1112namespace ns1 {
1113class Class1
1114{
1115void fun();
1116};
1117}
1118
1119using ns1::Class1;
1120
1121void Class1::fun() {}
1122)cpp";
1123std::string fileName = "main.cpp";
1124WriteFile(fileName, testSource);
1125const char *Args[] = {"-xc++"};
1126ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1127nullptr, 0, TUFlags);
1128
1129std::optional<CXCursor> typeRefCsr;
1130Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1131if (cursor.kind == CXCursor_TypeRef) {
1132typeRefCsr.emplace(cursor);
1133}
1134return CXChildVisit_Recurse;
1135});
1136ASSERT_TRUE(typeRefCsr.has_value());
1137EXPECT_EQ(fromCXString(clang_getCursorSpelling(*typeRefCsr)),
1138"class ns1::Class1");
1139}
1140
1141TEST_F(LibclangParseTest, BinaryOperator) {
1142std::string Main = "main.cpp";
1143WriteFile(Main, "int foo() { return 5 + 9; }");
1144ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
11450, TUFlags);
1146
1147Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1148if (cursor.kind == CXCursor_BinaryOperator) {
1149EXPECT_EQ(clang_getCursorBinaryOperatorKind(cursor),
1150CXBinaryOperator_Add);
1151return CXChildVisit_Break;
1152}
1153
1154return CXChildVisit_Recurse;
1155});
1156}
1157
1158TEST_F(LibclangParseTest, UnaryOperator) {
1159std::string Main = "main.cpp";
1160WriteFile(Main, "int foo() { int a = 5; return a++; }");
1161ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
11620, TUFlags);
1163
1164Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1165if (cursor.kind == CXCursor_UnaryOperator) {
1166EXPECT_EQ(clang_getCursorUnaryOperatorKind(cursor),
1167CXUnaryOperator_PostInc);
1168return CXChildVisit_Break;
1169}
1170
1171return CXChildVisit_Recurse;
1172});
1173}
1174
1175TEST_F(LibclangParseTest, VisitStaticAssertDecl_noMessage) {
1176const char testSource[] = R"cpp(static_assert(true))cpp";
1177std::string fileName = "main.cpp";
1178WriteFile(fileName, testSource);
1179const char *Args[] = {"-xc++"};
1180ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1181nullptr, 0, TUFlags);
1182
1183std::optional<CXCursor> staticAssertCsr;
1184Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1185if (cursor.kind == CXCursor_StaticAssert) {
1186staticAssertCsr.emplace(cursor);
1187return CXChildVisit_Break;
1188}
1189return CXChildVisit_Recurse;
1190});
1191ASSERT_TRUE(staticAssertCsr.has_value());
1192Traverse(*staticAssertCsr, [](CXCursor cursor, CXCursor parent) {
1193EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr);
1194return CXChildVisit_Break;
1195});
1196EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), "");
1197}
1198
1199TEST_F(LibclangParseTest, VisitStaticAssertDecl_exprMessage) {
1200const char testSource[] = R"cpp(
1201template <unsigned s>
1202constexpr unsigned size(const char (&)[s])
1203{
1204return s - 1;
1205}
1206
1207struct Message {
1208static constexpr char message[] = "Hello World!";
1209constexpr const char* data() const { return message;}
1210constexpr unsigned size() const
1211{
1212return ::size(message);
1213}
1214};
1215Message message;
1216static_assert(true, message);
1217)cpp";
1218std::string fileName = "main.cpp";
1219WriteFile(fileName, testSource);
1220const char *Args[] = {"-xc++", "-std=c++26"};
1221ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args,
1222std::size(Args), nullptr, 0, TUFlags);
1223ASSERT_EQ(clang_getNumDiagnostics(ClangTU), 0u);
1224std::optional<CXCursor> staticAssertCsr;
1225Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1226if (cursor.kind == CXCursor_StaticAssert) {
1227staticAssertCsr.emplace(cursor);
1228}
1229return CXChildVisit_Continue;
1230});
1231ASSERT_TRUE(staticAssertCsr.has_value());
1232int argCnt = 0;
1233Traverse(*staticAssertCsr, [&argCnt](CXCursor cursor, CXCursor parent) {
1234switch (argCnt) {
1235case 0:
1236EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr);
1237break;
1238case 1:
1239EXPECT_EQ(cursor.kind, CXCursor_DeclRefExpr);
1240break;
1241}
1242++argCnt;
1243return CXChildVisit_Continue;
1244});
1245ASSERT_EQ(argCnt, 2);
1246EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), "");
1247}
1248
1249TEST_F(LibclangParseTest, ExposesAnnotateArgs) {
1250const char testSource[] = R"cpp(
1251[[clang::annotate("category", 42)]]
1252void func() {}
1253)cpp";
1254std::string fileName = "main.cpp";
1255WriteFile(fileName, testSource);
1256
1257const char *Args[] = {"-xc++"};
1258ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1259nullptr, 0, TUFlags);
1260
1261int attrCount = 0;
1262
1263Traverse(
1264[&attrCount](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1265if (cursor.kind == CXCursor_AnnotateAttr) {
1266int childCount = 0;
1267clang_visitChildren(
1268cursor,
1269[](CXCursor child, CXCursor,
1270CXClientData data) -> CXChildVisitResult {
1271int *pcount = static_cast<int *>(data);
1272
1273// we only expect one argument here, so bail otherwise
1274EXPECT_EQ(*pcount, 0);
1275
1276auto *result = clang_Cursor_Evaluate(child);
1277EXPECT_NE(result, nullptr);
1278EXPECT_EQ(clang_EvalResult_getAsInt(result), 42);
1279clang_EvalResult_dispose(result);
1280
1281++*pcount;
1282
1283return CXChildVisit_Recurse;
1284},
1285&childCount);
1286attrCount++;
1287return CXChildVisit_Continue;
1288}
1289return CXChildVisit_Recurse;
1290});
1291
1292EXPECT_EQ(attrCount, 1);
1293}
1294
1295TEST_F(LibclangParseTest, clang_getSpellingLocation) {
1296std::string fileName = "main.c";
1297WriteFile(fileName, "#define X(value) int x = value;\nX(42)\n");
1298
1299ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), nullptr, 0,
1300nullptr, 0, TUFlags);
1301
1302int declarationCount = 0;
1303Traverse([&declarationCount](CXCursor cursor,
1304CXCursor parent) -> CXChildVisitResult {
1305if (cursor.kind == CXCursor_VarDecl) {
1306declarationCount++;
1307
1308CXSourceLocation cxl = clang_getCursorLocation(cursor);
1309unsigned line;
1310
1311// We expect clang_getFileLocation to return the expansion location,
1312// whereas clang_getSpellingLocation should resolve the macro expansion
1313// and return the location of the macro definition.
1314clang_getFileLocation(cxl, nullptr, &line, nullptr, nullptr);
1315EXPECT_EQ(line, 2U);
1316clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
1317EXPECT_EQ(line, 1U);
1318}
1319
1320return CXChildVisit_Recurse;
1321});
1322
1323EXPECT_EQ(declarationCount, 1);
1324}
1325
1326class LibclangRewriteTest : public LibclangParseTest {
1327public:
1328CXRewriter Rew = nullptr;
1329std::string Filename;
1330CXFile File = nullptr;
1331
1332void SetUp() override {
1333LibclangParseTest::SetUp();
1334Filename = "file.cpp";
1335WriteFile(Filename, "int main() { return 0; }");
1336ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0,
1337nullptr, 0, TUFlags);
1338Rew = clang_CXRewriter_create(ClangTU);
1339File = clang_getFile(ClangTU, Filename.c_str());
1340}
1341void TearDown() override {
1342clang_CXRewriter_dispose(Rew);
1343LibclangParseTest::TearDown();
1344}
1345};
1346
1347static std::string getFileContent(const std::string& Filename) {
1348std::ifstream RewrittenFile(Filename);
1349std::string RewrittenFileContent;
1350std::string Line;
1351while (std::getline(RewrittenFile, Line)) {
1352if (RewrittenFileContent.empty())
1353RewrittenFileContent = Line;
1354else {
1355RewrittenFileContent += "\n" + Line;
1356}
1357}
1358return RewrittenFileContent;
1359}
1360
1361TEST_F(LibclangRewriteTest, RewriteReplace) {
1362CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1363CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1364CXSourceRange Rng = clang_getRange(B, E);
1365
1366clang_CXRewriter_replaceText(Rew, Rng, "MAIN");
1367
1368ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1369EXPECT_EQ(getFileContent(Filename), "int MAIN() { return 0; }");
1370}
1371
1372TEST_F(LibclangRewriteTest, RewriteReplaceShorter) {
1373CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1374CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1375CXSourceRange Rng = clang_getRange(B, E);
1376
1377clang_CXRewriter_replaceText(Rew, Rng, "foo");
1378
1379ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1380EXPECT_EQ(getFileContent(Filename), "int foo() { return 0; }");
1381}
1382
1383TEST_F(LibclangRewriteTest, RewriteReplaceLonger) {
1384CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1385CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1386CXSourceRange Rng = clang_getRange(B, E);
1387
1388clang_CXRewriter_replaceText(Rew, Rng, "patatino");
1389
1390ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1391EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }");
1392}
1393
1394TEST_F(LibclangRewriteTest, RewriteInsert) {
1395CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5);
1396
1397clang_CXRewriter_insertTextBefore(Rew, Loc, "ro");
1398
1399ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1400EXPECT_EQ(getFileContent(Filename), "int romain() { return 0; }");
1401}
1402
1403TEST_F(LibclangRewriteTest, RewriteRemove) {
1404CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1405CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1406CXSourceRange Rng = clang_getRange(B, E);
1407
1408clang_CXRewriter_removeText(Rew, Rng);
1409
1410ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1411EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
1412}
1413