llvm-project

Форк
0
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

27
TEST(libclang, clang_parseTranslationUnit2_InvalidArgs) {
28
  EXPECT_EQ(CXError_InvalidArguments,
29
            clang_parseTranslationUnit2(nullptr, nullptr, nullptr, 0, nullptr,
30
                                        0, 0, nullptr));
31
}
32

33
TEST(libclang, clang_createTranslationUnit_InvalidArgs) {
34
  EXPECT_EQ(nullptr, clang_createTranslationUnit(nullptr, nullptr));
35
}
36

37
TEST(libclang, clang_createTranslationUnit2_InvalidArgs) {
38
  EXPECT_EQ(CXError_InvalidArguments,
39
            clang_createTranslationUnit2(nullptr, nullptr, nullptr));
40

41
  CXTranslationUnit TU = reinterpret_cast<CXTranslationUnit>(1);
42
  EXPECT_EQ(CXError_InvalidArguments,
43
            clang_createTranslationUnit2(nullptr, nullptr, &TU));
44
  EXPECT_EQ(nullptr, TU);
45
}
46

47
namespace {
48
struct TestVFO {
49
  const char *Contents;
50
  CXVirtualFileOverlay VFO;
51

52
  TestVFO(const char *Contents) : Contents(Contents) {
53
    VFO = clang_VirtualFileOverlay_create(0);
54
  }
55

56
  void map(const char *VPath, const char *RPath) {
57
    CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
58
    EXPECT_EQ(Err, CXError_Success);
59
  }
60

61
  void mapError(const char *VPath, const char *RPath, CXErrorCode ExpErr) {
62
    CXErrorCode Err = clang_VirtualFileOverlay_addFileMapping(VFO, VPath, RPath);
63
    EXPECT_EQ(Err, ExpErr);
64
  }
65

66
  ~TestVFO() {
67
    if (Contents) {
68
      char *BufPtr;
69
      unsigned BufSize;
70
      clang_VirtualFileOverlay_writeToBuffer(VFO, 0, &BufPtr, &BufSize);
71
      std::string BufStr(BufPtr, BufSize);
72
      EXPECT_STREQ(Contents, BufStr.c_str());
73
      clang_free(BufPtr);
74
    }
75
    clang_VirtualFileOverlay_dispose(VFO);
76
  }
77
};
78
}
79

80
TEST(libclang, VirtualFileOverlay_Basic) {
81
  const char *contents =
82
      "{\n"
83
      "  'version': 0,\n"
84
      "  'roots': [\n"
85
      "    {\n"
86
      "      'type': 'directory',\n"
87
      "      'name': \"/path/virtual\",\n"
88
      "      'contents': [\n"
89
      "        {\n"
90
      "          'type': 'file',\n"
91
      "          'name': \"foo.h\",\n"
92
      "          'external-contents': \"/real/foo.h\"\n"
93
      "        }\n"
94
      "      ]\n"
95
      "    }\n"
96
      "  ]\n"
97
      "}\n";
98
  TestVFO T(contents);
99
  T.map("/path/virtual/foo.h", "/real/foo.h");
100
}
101

102
TEST(libclang, VirtualFileOverlay_Unicode) {
103
  const char *contents =
104
      "{\n"
105
      "  'version': 0,\n"
106
      "  'roots': [\n"
107
      "    {\n"
108
      "      'type': 'directory',\n"
109
      "      'name': \"/path/\\u266B\",\n"
110
      "      'contents': [\n"
111
      "        {\n"
112
      "          'type': 'file',\n"
113
      "          'name': \"\\u2602.h\",\n"
114
      "          'external-contents': \"/real/\\u2602.h\"\n"
115
      "        }\n"
116
      "      ]\n"
117
      "    }\n"
118
      "  ]\n"
119
      "}\n";
120
  TestVFO T(contents);
121
  T.map("/path/♫/☂.h", "/real/☂.h");
122
}
123

124
TEST(libclang, VirtualFileOverlay_InvalidArgs) {
125
  TestVFO T(nullptr);
126
  T.mapError("/path/./virtual/../foo.h", "/real/foo.h",
127
             CXError_InvalidArguments);
128
}
129

130
TEST(libclang, VirtualFileOverlay_RemapDirectories) {
131
  const char *contents =
132
      "{\n"
133
      "  'version': 0,\n"
134
      "  'roots': [\n"
135
      "    {\n"
136
      "      'type': 'directory',\n"
137
      "      'name': \"/another/dir\",\n"
138
      "      'contents': [\n"
139
      "        {\n"
140
      "          'type': 'file',\n"
141
      "          'name': \"foo2.h\",\n"
142
      "          'external-contents': \"/real/foo2.h\"\n"
143
      "        }\n"
144
      "      ]\n"
145
      "    },\n"
146
      "    {\n"
147
      "      'type': 'directory',\n"
148
      "      'name': \"/path/virtual/dir\",\n"
149
      "      'contents': [\n"
150
      "        {\n"
151
      "          'type': 'file',\n"
152
      "          'name': \"foo1.h\",\n"
153
      "          'external-contents': \"/real/foo1.h\"\n"
154
      "        },\n"
155
      "        {\n"
156
      "          'type': 'file',\n"
157
      "          'name': \"foo3.h\",\n"
158
      "          'external-contents': \"/real/foo3.h\"\n"
159
      "        },\n"
160
      "        {\n"
161
      "          'type': 'directory',\n"
162
      "          'name': \"in/subdir\",\n"
163
      "          'contents': [\n"
164
      "            {\n"
165
      "              'type': 'file',\n"
166
      "              'name': \"foo4.h\",\n"
167
      "              'external-contents': \"/real/foo4.h\"\n"
168
      "            }\n"
169
      "          ]\n"
170
      "        }\n"
171
      "      ]\n"
172
      "    }\n"
173
      "  ]\n"
174
      "}\n";
175
  TestVFO T(contents);
176
  T.map("/path/virtual/dir/foo1.h", "/real/foo1.h");
177
  T.map("/another/dir/foo2.h", "/real/foo2.h");
178
  T.map("/path/virtual/dir/foo3.h", "/real/foo3.h");
179
  T.map("/path/virtual/dir/in/subdir/foo4.h", "/real/foo4.h");
180
}
181

182
TEST(libclang, VirtualFileOverlay_CaseInsensitive) {
183
  const char *contents =
184
      "{\n"
185
      "  'version': 0,\n"
186
      "  'case-sensitive': 'false',\n"
187
      "  'roots': [\n"
188
      "    {\n"
189
      "      'type': 'directory',\n"
190
      "      'name': \"/path/virtual\",\n"
191
      "      'contents': [\n"
192
      "        {\n"
193
      "          'type': 'file',\n"
194
      "          'name': \"foo.h\",\n"
195
      "          'external-contents': \"/real/foo.h\"\n"
196
      "        }\n"
197
      "      ]\n"
198
      "    }\n"
199
      "  ]\n"
200
      "}\n";
201
  TestVFO T(contents);
202
  T.map("/path/virtual/foo.h", "/real/foo.h");
203
  clang_VirtualFileOverlay_setCaseSensitivity(T.VFO, false);
204
}
205

206
TEST(libclang, VirtualFileOverlay_SharedPrefix) {
207
  const char *contents =
208
      "{\n"
209
      "  'version': 0,\n"
210
      "  'roots': [\n"
211
      "    {\n"
212
      "      'type': 'directory',\n"
213
      "      'name': \"/path/foo\",\n"
214
      "      'contents': [\n"
215
      "        {\n"
216
      "          'type': 'file',\n"
217
      "          'name': \"bar\",\n"
218
      "          'external-contents': \"/real/bar\"\n"
219
      "        },\n"
220
      "        {\n"
221
      "          'type': 'file',\n"
222
      "          'name': \"bar.h\",\n"
223
      "          'external-contents': \"/real/bar.h\"\n"
224
      "        }\n"
225
      "      ]\n"
226
      "    },\n"
227
      "    {\n"
228
      "      'type': 'directory',\n"
229
      "      'name': \"/path/foobar\",\n"
230
      "      'contents': [\n"
231
      "        {\n"
232
      "          'type': 'file',\n"
233
      "          'name': \"baz.h\",\n"
234
      "          'external-contents': \"/real/baz.h\"\n"
235
      "        }\n"
236
      "      ]\n"
237
      "    },\n"
238
      "    {\n"
239
      "      'type': 'directory',\n"
240
      "      'name': \"/path\",\n"
241
      "      'contents': [\n"
242
      "        {\n"
243
      "          'type': 'file',\n"
244
      "          'name': \"foobarbaz.h\",\n"
245
      "          'external-contents': \"/real/foobarbaz.h\"\n"
246
      "        }\n"
247
      "      ]\n"
248
      "    }\n"
249
      "  ]\n"
250
      "}\n";
251
  TestVFO T(contents);
252
  T.map("/path/foo/bar.h", "/real/bar.h");
253
  T.map("/path/foo/bar", "/real/bar");
254
  T.map("/path/foobar/baz.h", "/real/baz.h");
255
  T.map("/path/foobarbaz.h", "/real/foobarbaz.h");
256
}
257

258
TEST(libclang, VirtualFileOverlay_AdjacentDirectory) {
259
  const char *contents =
260
      "{\n"
261
      "  'version': 0,\n"
262
      "  'roots': [\n"
263
      "    {\n"
264
      "      'type': 'directory',\n"
265
      "      'name': \"/path/dir1\",\n"
266
      "      'contents': [\n"
267
      "        {\n"
268
      "          'type': 'file',\n"
269
      "          'name': \"foo.h\",\n"
270
      "          'external-contents': \"/real/foo.h\"\n"
271
      "        },\n"
272
      "        {\n"
273
      "          'type': 'directory',\n"
274
      "          'name': \"subdir\",\n"
275
      "          'contents': [\n"
276
      "            {\n"
277
      "              'type': 'file',\n"
278
      "              'name': \"bar.h\",\n"
279
      "              'external-contents': \"/real/bar.h\"\n"
280
      "            }\n"
281
      "          ]\n"
282
      "        }\n"
283
      "      ]\n"
284
      "    },\n"
285
      "    {\n"
286
      "      'type': 'directory',\n"
287
      "      'name': \"/path/dir2\",\n"
288
      "      'contents': [\n"
289
      "        {\n"
290
      "          'type': 'file',\n"
291
      "          'name': \"baz.h\",\n"
292
      "          'external-contents': \"/real/baz.h\"\n"
293
      "        }\n"
294
      "      ]\n"
295
      "    }\n"
296
      "  ]\n"
297
      "}\n";
298
  TestVFO T(contents);
299
  T.map("/path/dir1/foo.h", "/real/foo.h");
300
  T.map("/path/dir1/subdir/bar.h", "/real/bar.h");
301
  T.map("/path/dir2/baz.h", "/real/baz.h");
302
}
303

304
TEST(libclang, VirtualFileOverlay_TopLevel) {
305
  const char *contents =
306
      "{\n"
307
      "  'version': 0,\n"
308
      "  'roots': [\n"
309
      "    {\n"
310
      "      'type': 'directory',\n"
311
      "      'name': \"/\",\n"
312
      "      'contents': [\n"
313
      "        {\n"
314
      "          'type': 'file',\n"
315
      "          'name': \"foo.h\",\n"
316
      "          'external-contents': \"/real/foo.h\"\n"
317
      "        }\n"
318
      "      ]\n"
319
      "    }\n"
320
      "  ]\n"
321
      "}\n";
322
  TestVFO T(contents);
323
  T.map("/foo.h", "/real/foo.h");
324
}
325

326
TEST(libclang, VirtualFileOverlay_Empty) {
327
  const char *contents =
328
      "{\n"
329
      "  'version': 0,\n"
330
      "  'roots': [\n"
331
      "  ]\n"
332
      "}\n";
333
  TestVFO T(contents);
334
}
335

336
TEST(libclang, ModuleMapDescriptor) {
337
  const char *Contents =
338
    "framework module TestFrame {\n"
339
    "  umbrella header \"TestFrame.h\"\n"
340
    "\n"
341
    "  export *\n"
342
    "  module * { export * }\n"
343
    "}\n";
344

345
  CXModuleMapDescriptor MMD = clang_ModuleMapDescriptor_create(0);
346

347
  clang_ModuleMapDescriptor_setFrameworkModuleName(MMD, "TestFrame");
348
  clang_ModuleMapDescriptor_setUmbrellaHeader(MMD, "TestFrame.h");
349

350
  char *BufPtr;
351
  unsigned BufSize;
352
  clang_ModuleMapDescriptor_writeToBuffer(MMD, 0, &BufPtr, &BufSize);
353
  std::string BufStr(BufPtr, BufSize);
354
  EXPECT_STREQ(Contents, BufStr.c_str());
355
  clang_free(BufPtr);
356
  clang_ModuleMapDescriptor_dispose(MMD);
357
}
358

359
TEST_F(LibclangParseTest, GlobalOptions) {
360
  EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None);
361
}
362

363
class LibclangIndexOptionsTest : public LibclangParseTest {
364
  virtual void AdjustOptions(CXIndexOptions &Opts) {}
365

366
protected:
367
  void CreateIndex() override {
368
    CXIndexOptions Opts;
369
    memset(&Opts, 0, sizeof(Opts));
370
    Opts.Size = sizeof(CXIndexOptions);
371
    AdjustOptions(Opts);
372
    Index = clang_createIndexWithOptions(&Opts);
373
    ASSERT_TRUE(Index);
374
  }
375
};
376

377
TEST_F(LibclangIndexOptionsTest, GlobalOptions) {
378
  EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None);
379
}
380

381
class LibclangIndexingEnabledIndexOptionsTest
382
    : public LibclangIndexOptionsTest {
383
  void AdjustOptions(CXIndexOptions &Opts) override {
384
    Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled;
385
  }
386
};
387

388
TEST_F(LibclangIndexingEnabledIndexOptionsTest, GlobalOptions) {
389
  EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
390
            CXGlobalOpt_ThreadBackgroundPriorityForIndexing);
391
}
392

393
class LibclangIndexingDisabledEditingEnabledIndexOptionsTest
394
    : public LibclangIndexOptionsTest {
395
  void AdjustOptions(CXIndexOptions &Opts) override {
396
    Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Disabled;
397
    Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled;
398
  }
399
};
400

401
TEST_F(LibclangIndexingDisabledEditingEnabledIndexOptionsTest, GlobalOptions) {
402
  EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
403
            CXGlobalOpt_ThreadBackgroundPriorityForEditing);
404
}
405

406
class LibclangBothEnabledIndexOptionsTest : public LibclangIndexOptionsTest {
407
  void AdjustOptions(CXIndexOptions &Opts) override {
408
    Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled;
409
    Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled;
410
  }
411
};
412

413
TEST_F(LibclangBothEnabledIndexOptionsTest, GlobalOptions) {
414
  EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index),
415
            CXGlobalOpt_ThreadBackgroundPriorityForAll);
416
}
417

418
class LibclangPreambleStorageTest : public LibclangParseTest {
419
  std::string Main = "main.cpp";
420

421
protected:
422
  std::string PreambleDir;
423
  void InitializePreambleDir() {
424
    llvm::SmallString<128> PathBuffer(TestDir);
425
    llvm::sys::path::append(PathBuffer, "preambles");
426
    namespace fs = llvm::sys::fs;
427
    ASSERT_FALSE(fs::create_directory(PathBuffer, false, fs::perms::owner_all));
428

429
    PreambleDir = static_cast<std::string>(PathBuffer);
430
    FilesAndDirsToRemove.insert(PreambleDir);
431
  }
432

433
public:
434
  void CountPreamblesInPreambleDir(int PreambleCount) {
435
    // For some reason, the preamble is not created without '\n' before `int`.
436
    WriteFile(Main, "\nint main() {}");
437

438
    TUFlags |= CXTranslationUnit_CreatePreambleOnFirstParse;
439
    ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
440
                                         nullptr, 0, TUFlags);
441

442
    int FileCount = 0;
443

444
    namespace fs = llvm::sys::fs;
445
    std::error_code EC;
446
    for (fs::directory_iterator File(PreambleDir, EC), FileEnd;
447
         File != FileEnd && !EC; File.increment(EC)) {
448
      ++FileCount;
449

450
      EXPECT_EQ(File->type(), fs::file_type::regular_file);
451

452
      const auto Filename = llvm::sys::path::filename(File->path());
453
      EXPECT_EQ(Filename.size(), std::strlen("preamble-%%%%%%.pch"));
454
      EXPECT_TRUE(Filename.starts_with("preamble-"));
455
      EXPECT_TRUE(Filename.ends_with(".pch"));
456

457
      const auto Status = File->status();
458
      ASSERT_TRUE(Status);
459
      if (false) {
460
        // The permissions assertion below fails, because the .pch.tmp file is
461
        // created with default permissions and replaces the .pch file along
462
        // with its permissions. Therefore the permissions set in
463
        // TempPCHFile::create() don't matter in the end.
464
        EXPECT_EQ(Status->permissions(), fs::owner_read | fs::owner_write);
465
      }
466
    }
467

468
    EXPECT_EQ(FileCount, PreambleCount);
469
  }
470
};
471

472
class LibclangNotOverriddenPreambleStoragePathTest
473
    : public LibclangPreambleStorageTest {
474
protected:
475
  void CreateIndex() override {
476
    InitializePreambleDir();
477
    LibclangPreambleStorageTest::CreateIndex();
478
  }
479
};
480

481
class LibclangSetPreambleStoragePathTest : public LibclangPreambleStorageTest {
482
  virtual bool StorePreamblesInMemory() { return false; }
483
  virtual const char *PreambleStoragePath() = 0;
484

485
protected:
486
  void CreateIndex() override {
487
    InitializePreambleDir();
488

489
    CXIndexOptions Opts{};
490
    Opts.Size = sizeof(CXIndexOptions);
491
    Opts.StorePreamblesInMemory = StorePreamblesInMemory();
492
    Opts.PreambleStoragePath = PreambleStoragePath();
493
    Index = clang_createIndexWithOptions(&Opts);
494
    ASSERT_TRUE(Index);
495
  }
496
};
497

498
class LibclangNullPreambleStoragePathTest
499
    : public LibclangSetPreambleStoragePathTest {
500
  const char *PreambleStoragePath() override { return nullptr; }
501
};
502
class LibclangEmptyPreambleStoragePathTest
503
    : public LibclangSetPreambleStoragePathTest {
504
  const char *PreambleStoragePath() override { return ""; }
505
};
506
class LibclangPreambleDirPreambleStoragePathTest
507
    : public LibclangSetPreambleStoragePathTest {
508
  const char *PreambleStoragePath() override { return PreambleDir.c_str(); }
509
};
510

511
class LibclangStoreInMemoryNullPreambleStoragePathTest
512
    : public LibclangNullPreambleStoragePathTest {
513
  bool StorePreamblesInMemory() override { return true; }
514
};
515
class LibclangStoreInMemoryEmptyPreambleStoragePathTest
516
    : public LibclangEmptyPreambleStoragePathTest {
517
  bool StorePreamblesInMemory() override { return true; }
518
};
519
class LibclangStoreInMemoryPreambleDirPreambleStoragePathTest
520
    : public LibclangPreambleDirPreambleStoragePathTest {
521
  bool StorePreamblesInMemory() override { return true; }
522
};
523

524
TEST_F(LibclangNotOverriddenPreambleStoragePathTest, CountPreambles) {
525
  CountPreamblesInPreambleDir(0);
526
}
527
TEST_F(LibclangNullPreambleStoragePathTest, CountPreambles) {
528
  CountPreamblesInPreambleDir(0);
529
}
530
TEST_F(LibclangEmptyPreambleStoragePathTest, CountPreambles) {
531
  CountPreamblesInPreambleDir(0);
532
}
533
TEST_F(LibclangPreambleDirPreambleStoragePathTest, CountPreambles) {
534
  CountPreamblesInPreambleDir(1);
535
}
536
TEST_F(LibclangStoreInMemoryNullPreambleStoragePathTest, CountPreambles) {
537
  CountPreamblesInPreambleDir(0);
538
}
539
TEST_F(LibclangStoreInMemoryEmptyPreambleStoragePathTest, CountPreambles) {
540
  CountPreamblesInPreambleDir(0);
541
}
542
TEST_F(LibclangStoreInMemoryPreambleDirPreambleStoragePathTest,
543
       CountPreambles) {
544
  CountPreamblesInPreambleDir(0);
545
}
546

547
TEST_F(LibclangParseTest, AllSkippedRanges) {
548
  std::string Header = "header.h", Main = "main.cpp";
549
  WriteFile(Header,
550
    "#ifdef MANGOS\n"
551
    "printf(\"mmm\");\n"
552
    "#endif");
553
  WriteFile(Main,
554
    "#include \"header.h\"\n"
555
    "#ifdef KIWIS\n"
556
    "printf(\"mmm!!\");\n"
557
    "#endif");
558

559
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
560
                                       nullptr, 0, TUFlags);
561

562
  CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
563
  EXPECT_EQ(2U, Ranges->count);
564
  
565
  CXSourceLocation cxl;
566
  unsigned line;
567
  cxl = clang_getRangeStart(Ranges->ranges[0]);
568
  clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
569
  EXPECT_EQ(1U, line);
570
  cxl = clang_getRangeEnd(Ranges->ranges[0]);
571
  clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
572
  EXPECT_EQ(3U, line);
573

574
  cxl = clang_getRangeStart(Ranges->ranges[1]);
575
  clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
576
  EXPECT_EQ(2U, line);
577
  cxl = clang_getRangeEnd(Ranges->ranges[1]);
578
  clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
579
  EXPECT_EQ(4U, line);
580

581
  clang_disposeSourceRangeList(Ranges);
582
}
583

584
TEST_F(LibclangParseTest, EvaluateChildExpression) {
585
  std::string Main = "main.m";
586
  WriteFile(Main, "#define kFOO @\"foo\"\n"
587
                  "void foobar(void) {\n"
588
                  " {kFOO;}\n"
589
                  "}\n");
590
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
591
                                       0, TUFlags);
592

593
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
594
  clang_visitChildren(
595
      C,
596
      [](CXCursor cursor, CXCursor parent,
597
         CXClientData client_data) -> CXChildVisitResult {
598
        if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
599
          int numberedStmt = 0;
600
          clang_visitChildren(
601
              cursor,
602
              [](CXCursor cursor, CXCursor parent,
603
                 CXClientData client_data) -> CXChildVisitResult {
604
                int &numberedStmt = *((int *)client_data);
605
                if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt) {
606
                  if (numberedStmt) {
607
                    CXEvalResult RE = clang_Cursor_Evaluate(cursor);
608
                    EXPECT_NE(RE, nullptr);
609
                    EXPECT_EQ(clang_EvalResult_getKind(RE),
610
                              CXEval_ObjCStrLiteral);
611
                    clang_EvalResult_dispose(RE);
612
                    return CXChildVisit_Break;
613
                  }
614
                  numberedStmt++;
615
                }
616
                return CXChildVisit_Recurse;
617
              },
618
              &numberedStmt);
619
          EXPECT_EQ(numberedStmt, 1);
620
        }
621
        return CXChildVisit_Continue;
622
      },
623
      nullptr);
624
}
625

626
class LibclangReparseTest : public LibclangParseTest {
627
public:
628
  void DisplayDiagnostics() {
629
    unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU);
630
    for (unsigned i = 0; i < NumDiagnostics; ++i) {
631
      auto Diag = clang_getDiagnostic(ClangTU, i);
632
      LLVM_DEBUG(llvm::dbgs()
633
                 << clang_getCString(clang_formatDiagnostic(
634
                        Diag, clang_defaultDiagnosticDisplayOptions()))
635
                 << "\n");
636
      clang_disposeDiagnostic(Diag);
637
    }
638
  }
639
  bool ReparseTU(unsigned num_unsaved_files, CXUnsavedFile* unsaved_files) {
640
    if (clang_reparseTranslationUnit(ClangTU, num_unsaved_files, unsaved_files,
641
                                     clang_defaultReparseOptions(ClangTU))) {
642
      LLVM_DEBUG(llvm::dbgs() << "Reparse failed\n");
643
      return false;
644
    }
645
    DisplayDiagnostics();
646
    return true;
647
  }
648
};
649

650
TEST_F(LibclangReparseTest, FileName) {
651
  std::string CppName = "main.cpp";
652
  WriteFile(CppName, "int main() {}");
653
  ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
654
                                       nullptr, 0, TUFlags);
655
  CXFile cxf = clang_getFile(ClangTU, CppName.c_str());
656

657
  CXString cxname = clang_getFileName(cxf);
658
  ASSERT_STREQ(clang_getCString(cxname), CppName.c_str());
659
  clang_disposeString(cxname);
660

661
  cxname = clang_File_tryGetRealPathName(cxf);
662
  ASSERT_TRUE(llvm::StringRef(clang_getCString(cxname)).ends_with("main.cpp"));
663
  clang_disposeString(cxname);
664
}
665

666
TEST_F(LibclangReparseTest, Reparse) {
667
  const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
668
  const char *HeaderBottom = "\n};\n#endif\n";
669
  const char *CppFile = "#include \"HeaderFile.h\"\nint main() {"
670
                         " Foo foo; foo.bar = 7; foo.baz = 8; }\n";
671
  std::string HeaderName = "HeaderFile.h";
672
  std::string CppName = "CppFile.cpp";
673
  WriteFile(CppName, CppFile);
674
  WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
675

676
  ClangTU = clang_parseTranslationUnit(Index, CppName.c_str(), nullptr, 0,
677
                                       nullptr, 0, TUFlags);
678
  EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
679
  DisplayDiagnostics();
680

681
  // Immedaitely reparse.
682
  ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
683
  EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
684

685
  std::string NewHeaderContents =
686
      std::string(HeaderTop) + "int baz;" + HeaderBottom;
687
  WriteFile(HeaderName, NewHeaderContents);
688

689
  // Reparse after fix.
690
  ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
691
  EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
692
}
693

694
TEST_F(LibclangReparseTest, ReparseWithModule) {
695
  const char *HeaderTop = "#ifndef H\n#define H\nstruct Foo { int bar;";
696
  const char *HeaderBottom = "\n};\n#endif\n";
697
  const char *MFile = "#include \"HeaderFile.h\"\nint main() {"
698
                         " struct Foo foo; foo.bar = 7; foo.baz = 8; }\n";
699
  const char *ModFile = "module A { header \"HeaderFile.h\" }\n";
700
  std::string HeaderName = "HeaderFile.h";
701
  std::string MName = "MFile.m";
702
  std::string ModName = "module.modulemap";
703
  WriteFile(MName, MFile);
704
  WriteFile(HeaderName, std::string(HeaderTop) + HeaderBottom);
705
  WriteFile(ModName, ModFile);
706

707
  // Removing recursively is necessary to delete the module cache.
708
  RemoveTestDirRecursivelyDuringTeardown = true;
709
  std::string ModulesCache = std::string("-fmodules-cache-path=") + TestDir;
710
  const char *Args[] = { "-fmodules", ModulesCache.c_str(),
711
                         "-I", TestDir.c_str() };
712
  int NumArgs = std::size(Args);
713
  ClangTU = clang_parseTranslationUnit(Index, MName.c_str(), Args, NumArgs,
714
                                       nullptr, 0, TUFlags);
715
  EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
716
  DisplayDiagnostics();
717

718
  // Immedaitely reparse.
719
  ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
720
  EXPECT_EQ(1U, clang_getNumDiagnostics(ClangTU));
721

722
  std::string NewHeaderContents =
723
      std::string(HeaderTop) + "int baz;" + HeaderBottom;
724
  WriteFile(HeaderName, NewHeaderContents);
725

726
  // Reparse after fix.
727
  ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
728
  EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
729
}
730

731
TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) {
732
  // Provide a fake GCC 99.9.9 standard library that always overrides any local
733
  // GCC installation.
734
  std::string EmptyFiles[] = {"lib/gcc/arm-linux-gnueabi/99.9.9/crtbegin.o",
735
                              "include/arm-linux-gnueabi/.keep",
736
                              "include/c++/99.9.9/vector"};
737

738
  for (auto &Name : EmptyFiles)
739
    WriteFile(Name, "\n");
740

741
  std::string Filename = "test.cc";
742
  WriteFile(Filename, "#include <vector>\n");
743

744
  std::string Clang = "bin/clang";
745
  WriteFile(Clang, "");
746

747
  const char *Argv[] = {Clang.c_str(), "-target", "arm-linux-gnueabi",
748
                        "-stdlib=libstdc++", "--gcc-toolchain="};
749

750
  EXPECT_EQ(CXError_Success,
751
            clang_parseTranslationUnit2FullArgv(Index, Filename.c_str(), Argv,
752
                                                std::size(Argv),
753
                                                nullptr, 0, TUFlags, &ClangTU));
754
  EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
755
  DisplayDiagnostics();
756
}
757

758
class LibclangPrintingPolicyTest : public LibclangParseTest {
759
public:
760
  CXPrintingPolicy Policy = nullptr;
761

762
  void SetUp() override {
763
    LibclangParseTest::SetUp();
764
    std::string File = "file.cpp";
765
    WriteFile(File, "int i;\n");
766
    ClangTU = clang_parseTranslationUnit(Index, File.c_str(), nullptr, 0,
767
                                         nullptr, 0, TUFlags);
768
    CXCursor TUCursor = clang_getTranslationUnitCursor(ClangTU);
769
    Policy = clang_getCursorPrintingPolicy(TUCursor);
770
  }
771
  void TearDown() override {
772
    clang_PrintingPolicy_dispose(Policy);
773
    LibclangParseTest::TearDown();
774
  }
775
};
776

777
TEST_F(LibclangPrintingPolicyTest, SetAndGetProperties) {
778
  for (unsigned Value = 0; Value < 2; ++Value) {
779
    for (int I = 0; I < CXPrintingPolicy_LastProperty; ++I) {
780
      auto Property = static_cast<enum CXPrintingPolicyProperty>(I);
781

782
      clang_PrintingPolicy_setProperty(Policy, Property, Value);
783
      EXPECT_EQ(Value, clang_PrintingPolicy_getProperty(Policy, Property));
784
    }
785
  }
786
}
787

788
TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) {
789
  std::string Header = "header.h", Main = "main.cpp";
790
  WriteFile(Header,
791
    "#ifdef MANGOS\n"
792
    "printf(\"mmm\");\n"
793
    "#endif");
794
  WriteFile(Main,
795
    "#include \"header.h\"\n"
796
    "#ifdef GUAVA\n"
797
    "#endif\n"
798
    "#ifdef KIWIS\n"
799
    "printf(\"mmm!!\");\n"
800
    "#endif");
801

802
  for (int i = 0; i != 3; ++i) {
803
    unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble;
804
    if (i == 2)
805
      flags |= CXTranslationUnit_CreatePreambleOnFirstParse;
806

807
    if (i != 0)
808
       clang_disposeTranslationUnit(ClangTU);  // dispose from previous iter
809

810
    // parse once
811
    ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
812
                                         nullptr, 0, flags);
813
    if (i != 0) {
814
      // reparse
815
      ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
816
    }
817

818
    // Check all ranges are there
819
    CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
820
    EXPECT_EQ(3U, Ranges->count);
821

822
    CXSourceLocation cxl;
823
    unsigned line;
824
    cxl = clang_getRangeStart(Ranges->ranges[0]);
825
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
826
    EXPECT_EQ(1U, line);
827
    cxl = clang_getRangeEnd(Ranges->ranges[0]);
828
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
829
    EXPECT_EQ(3U, line);
830

831
    cxl = clang_getRangeStart(Ranges->ranges[1]);
832
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
833
    EXPECT_EQ(2U, line);
834
    cxl = clang_getRangeEnd(Ranges->ranges[1]);
835
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
836
    EXPECT_EQ(3U, line);
837

838
    cxl = clang_getRangeStart(Ranges->ranges[2]);
839
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
840
    EXPECT_EQ(4U, line);
841
    cxl = clang_getRangeEnd(Ranges->ranges[2]);
842
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
843
    EXPECT_EQ(6U, line);
844

845
    clang_disposeSourceRangeList(Ranges);
846

847
    // Check obtaining ranges by each file works
848
    CXFile cxf = clang_getFile(ClangTU, Header.c_str());
849
    Ranges = clang_getSkippedRanges(ClangTU, cxf);
850
    EXPECT_EQ(1U, Ranges->count);
851
    cxl = clang_getRangeStart(Ranges->ranges[0]);
852
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
853
    EXPECT_EQ(1U, line);
854
    clang_disposeSourceRangeList(Ranges);
855

856
    cxf = clang_getFile(ClangTU, Main.c_str());
857
    Ranges = clang_getSkippedRanges(ClangTU, cxf);
858
    EXPECT_EQ(2U, Ranges->count);
859
    cxl = clang_getRangeStart(Ranges->ranges[0]);
860
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
861
    EXPECT_EQ(2U, line);
862
    cxl = clang_getRangeStart(Ranges->ranges[1]);
863
    clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
864
    EXPECT_EQ(4U, line);
865
    clang_disposeSourceRangeList(Ranges);
866
  }
867
}
868

869
class LibclangSerializationTest : public LibclangParseTest {
870
public:
871
  bool SaveAndLoadTU(const std::string &Filename) {
872
    unsigned options = clang_defaultSaveOptions(ClangTU);
873
    if (clang_saveTranslationUnit(ClangTU, Filename.c_str(), options) !=
874
        CXSaveError_None) {
875
      LLVM_DEBUG(llvm::dbgs() << "Saving failed\n");
876
      return false;
877
    }
878

879
    clang_disposeTranslationUnit(ClangTU);
880

881
    ClangTU = clang_createTranslationUnit(Index, Filename.c_str());
882

883
    if (!ClangTU) {
884
      LLVM_DEBUG(llvm::dbgs() << "Loading failed\n");
885
      return false;
886
    }
887

888
    return true;
889
  }
890
};
891

892
TEST_F(LibclangSerializationTest, TokenKindsAreCorrectAfterLoading) {
893
  // Ensure that "class" is recognized as a keyword token after serializing
894
  // and reloading the AST, as it is not a keyword for the default LangOptions.
895
  std::string HeaderName = "test.h";
896
  WriteFile(HeaderName, "enum class Something {};");
897

898
  const char *Argv[] = {"-xc++-header", "-std=c++11"};
899

900
  ClangTU = clang_parseTranslationUnit(Index, HeaderName.c_str(), Argv,
901
                                       std::size(Argv), nullptr,
902
                                       0, TUFlags);
903

904
  auto CheckTokenKinds = [=]() {
905
    CXSourceRange Range =
906
        clang_getCursorExtent(clang_getTranslationUnitCursor(ClangTU));
907

908
    CXToken *Tokens;
909
    unsigned int NumTokens;
910
    clang_tokenize(ClangTU, Range, &Tokens, &NumTokens);
911

912
    ASSERT_EQ(6u, NumTokens);
913
    EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[0]));
914
    EXPECT_EQ(CXToken_Keyword, clang_getTokenKind(Tokens[1]));
915
    EXPECT_EQ(CXToken_Identifier, clang_getTokenKind(Tokens[2]));
916
    EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[3]));
917
    EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[4]));
918
    EXPECT_EQ(CXToken_Punctuation, clang_getTokenKind(Tokens[5]));
919

920
    clang_disposeTokens(ClangTU, Tokens, NumTokens);
921
  };
922

923
  CheckTokenKinds();
924

925
  std::string ASTName = "test.ast";
926
  WriteFile(ASTName, "");
927

928
  ASSERT_TRUE(SaveAndLoadTU(ASTName));
929

930
  CheckTokenKinds();
931
}
932

933
TEST_F(LibclangParseTest, clang_getVarDeclInitializer) {
934
  std::string Main = "main.cpp";
935
  WriteFile(Main, "int foo() { return 5; }; const int a = foo();");
936
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
937
                                       0, TUFlags);
938

939
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
940
  clang_visitChildren(
941
      C,
942
      [](CXCursor cursor, CXCursor parent,
943
         CXClientData client_data) -> CXChildVisitResult {
944
        if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
945
          const CXCursor Initializer = clang_Cursor_getVarDeclInitializer(cursor);
946
          EXPECT_FALSE(clang_Cursor_isNull(Initializer));
947
          CXString Spelling = clang_getCursorSpelling(Initializer);
948
          const char* const SpellingCSstr = clang_getCString(Spelling);
949
          EXPECT_TRUE(SpellingCSstr);
950
          EXPECT_EQ(std::string(SpellingCSstr), std::string("foo"));
951
          clang_disposeString(Spelling);
952
          return CXChildVisit_Break;
953
        }
954
        return CXChildVisit_Continue;
955
      },
956
      nullptr);
957
}
958

959
TEST_F(LibclangParseTest, clang_hasVarDeclGlobalStorageFalse) {
960
  std::string Main = "main.cpp";
961
  WriteFile(Main, "void foo() { int a; }");
962
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
963
                                       0, TUFlags);
964

965
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
966
  clang_visitChildren(
967
      C,
968
      [](CXCursor cursor, CXCursor parent,
969
         CXClientData client_data) -> CXChildVisitResult {
970
        if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
971
          EXPECT_FALSE(clang_Cursor_hasVarDeclGlobalStorage(cursor));
972
          return CXChildVisit_Break;
973
        }
974
        return CXChildVisit_Continue;
975
      },
976
      nullptr);
977
}
978

979
TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclGlobalStorageTrue) {
980
  std::string Main = "main.cpp";
981
  WriteFile(Main, "int a;");
982
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
983
                                       0, TUFlags);
984

985
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
986
  clang_visitChildren(
987
      C,
988
      [](CXCursor cursor, CXCursor parent,
989
         CXClientData client_data) -> CXChildVisitResult {
990
        if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
991
          EXPECT_TRUE(clang_Cursor_hasVarDeclGlobalStorage(cursor));
992
          return CXChildVisit_Break;
993
        }
994
        return CXChildVisit_Continue;
995
      },
996
      nullptr);
997
}
998

999
TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageFalse) {
1000
  std::string Main = "main.cpp";
1001
  WriteFile(Main, "int a;");
1002
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
1003
                                       0, TUFlags);
1004

1005
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
1006
  clang_visitChildren(
1007
      C,
1008
      [](CXCursor cursor, CXCursor parent,
1009
         CXClientData client_data) -> CXChildVisitResult {
1010
        if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
1011
          EXPECT_FALSE(clang_Cursor_hasVarDeclExternalStorage(cursor));
1012
          return CXChildVisit_Break;
1013
        }
1014
        return CXChildVisit_Continue;
1015
      },
1016
      nullptr);
1017
}
1018

1019
TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageTrue) {
1020
  std::string Main = "main.cpp";
1021
  WriteFile(Main, "extern int a;");
1022
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
1023
                                       0, TUFlags);
1024

1025
  CXCursor C = clang_getTranslationUnitCursor(ClangTU);
1026
  clang_visitChildren(
1027
      C,
1028
      [](CXCursor cursor, CXCursor parent,
1029
         CXClientData client_data) -> CXChildVisitResult {
1030
        if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
1031
          EXPECT_TRUE(clang_Cursor_hasVarDeclExternalStorage(cursor));
1032
          return CXChildVisit_Break;
1033
        }
1034
        return CXChildVisit_Continue;
1035
      },
1036
      nullptr);
1037
}
1038

1039
TEST_F(LibclangParseTest, clang_getUnqualifiedTypeRemovesQualifiers) {
1040
  std::string Header = "header.h";
1041
  WriteFile(Header, "void foo1(const int);\n"
1042
                    "void foo2(volatile int);\n"
1043
                    "void foo3(const volatile int);\n"
1044
                    "void foo4(int* const);\n"
1045
                    "void foo5(int* volatile);\n"
1046
                    "void foo6(int* restrict);\n"
1047
                    "void foo7(int* const volatile);\n"
1048
                    "void foo8(int* volatile restrict);\n"
1049
                    "void foo9(int* const restrict);\n"
1050
                    "void foo10(int* const volatile restrict);\n");
1051

1052
  auto is_qualified = [](CXType type) -> bool {
1053
    return clang_isConstQualifiedType(type) ||
1054
           clang_isVolatileQualifiedType(type) ||
1055
           clang_isRestrictQualifiedType(type);
1056
  };
1057

1058
  ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), nullptr, 0,
1059
                                       nullptr, 0, TUFlags);
1060

1061
  Traverse([&is_qualified](CXCursor cursor, CXCursor) {
1062
    if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
1063
      CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0);
1064
      EXPECT_TRUE(is_qualified(arg_type))
1065
          << "Input data '" << fromCXString(clang_getCursorSpelling(cursor))
1066
          << "' first argument does not have a qualified type.";
1067

1068
      CXType unqualified_arg_type = clang_getUnqualifiedType(arg_type);
1069
      EXPECT_FALSE(is_qualified(unqualified_arg_type))
1070
          << "The type '" << fromCXString(clang_getTypeSpelling(arg_type))
1071
          << "' was not unqualified after a call to clang_getUnqualifiedType.";
1072
    }
1073

1074
    return CXChildVisit_Continue;
1075
  });
1076
}
1077

1078
TEST_F(LibclangParseTest, clang_getNonReferenceTypeRemovesRefQualifiers) {
1079
  std::string Header = "header.h";
1080
  WriteFile(Header, "void foo1(int&);\n"
1081
                    "void foo2(int&&);\n");
1082

1083
  auto is_ref_qualified = [](CXType type) -> bool {
1084
    return (type.kind == CXType_LValueReference) ||
1085
           (type.kind == CXType_RValueReference);
1086
  };
1087

1088
  const char *Args[] = {"-xc++"};
1089
  ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), Args, 1, nullptr,
1090
                                       0, TUFlags);
1091

1092
  Traverse([&is_ref_qualified](CXCursor cursor, CXCursor) {
1093
    if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
1094
      CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0);
1095
      EXPECT_TRUE(is_ref_qualified(arg_type))
1096
          << "Input data '" << fromCXString(clang_getCursorSpelling(cursor))
1097
          << "' first argument does not have a ref-qualified type.";
1098

1099
      CXType non_reference_arg_type = clang_getNonReferenceType(arg_type);
1100
      EXPECT_FALSE(is_ref_qualified(non_reference_arg_type))
1101
          << "The type '" << fromCXString(clang_getTypeSpelling(arg_type))
1102
          << "' ref-qualifier was not removed after a call to "
1103
             "clang_getNonReferenceType.";
1104
    }
1105

1106
    return CXChildVisit_Continue;
1107
  });
1108
}
1109

1110
TEST_F(LibclangParseTest, VisitUsingTypeLoc) {
1111
  const char testSource[] = R"cpp(
1112
namespace ns1 {
1113
class Class1
1114
{
1115
    void fun();
1116
};
1117
}
1118

1119
using ns1::Class1;
1120

1121
void Class1::fun() {}
1122
)cpp";
1123
  std::string fileName = "main.cpp";
1124
  WriteFile(fileName, testSource);
1125
  const char *Args[] = {"-xc++"};
1126
  ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1127
                                       nullptr, 0, TUFlags);
1128

1129
  std::optional<CXCursor> typeRefCsr;
1130
  Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1131
    if (cursor.kind == CXCursor_TypeRef) {
1132
      typeRefCsr.emplace(cursor);
1133
    }
1134
    return CXChildVisit_Recurse;
1135
  });
1136
  ASSERT_TRUE(typeRefCsr.has_value());
1137
  EXPECT_EQ(fromCXString(clang_getCursorSpelling(*typeRefCsr)),
1138
            "class ns1::Class1");
1139
}
1140

1141
TEST_F(LibclangParseTest, BinaryOperator) {
1142
  std::string Main = "main.cpp";
1143
  WriteFile(Main, "int foo() { return 5 + 9; }");
1144
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
1145
                                       0, TUFlags);
1146

1147
  Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1148
    if (cursor.kind == CXCursor_BinaryOperator) {
1149
      EXPECT_EQ(clang_getCursorBinaryOperatorKind(cursor),
1150
                CXBinaryOperator_Add);
1151
      return CXChildVisit_Break;
1152
    }
1153

1154
    return CXChildVisit_Recurse;
1155
  });
1156
}
1157

1158
TEST_F(LibclangParseTest, UnaryOperator) {
1159
  std::string Main = "main.cpp";
1160
  WriteFile(Main, "int foo() { int a = 5; return a++; }");
1161
  ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
1162
                                       0, TUFlags);
1163

1164
  Traverse([](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1165
    if (cursor.kind == CXCursor_UnaryOperator) {
1166
      EXPECT_EQ(clang_getCursorUnaryOperatorKind(cursor),
1167
                CXUnaryOperator_PostInc);
1168
      return CXChildVisit_Break;
1169
    }
1170

1171
    return CXChildVisit_Recurse;
1172
  });
1173
}
1174

1175
TEST_F(LibclangParseTest, VisitStaticAssertDecl_noMessage) {
1176
  const char testSource[] = R"cpp(static_assert(true))cpp";
1177
  std::string fileName = "main.cpp";
1178
  WriteFile(fileName, testSource);
1179
  const char *Args[] = {"-xc++"};
1180
  ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1181
                                       nullptr, 0, TUFlags);
1182

1183
  std::optional<CXCursor> staticAssertCsr;
1184
  Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1185
    if (cursor.kind == CXCursor_StaticAssert) {
1186
      staticAssertCsr.emplace(cursor);
1187
      return CXChildVisit_Break;
1188
    }
1189
    return CXChildVisit_Recurse;
1190
  });
1191
  ASSERT_TRUE(staticAssertCsr.has_value());
1192
  Traverse(*staticAssertCsr, [](CXCursor cursor, CXCursor parent) {
1193
    EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr);
1194
    return CXChildVisit_Break;
1195
  });
1196
  EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), "");
1197
}
1198

1199
TEST_F(LibclangParseTest, VisitStaticAssertDecl_exprMessage) {
1200
  const char testSource[] = R"cpp(
1201
template <unsigned s>
1202
constexpr unsigned size(const char (&)[s])
1203
{
1204
    return s - 1;
1205
}
1206

1207
struct Message {
1208
    static constexpr char message[] = "Hello World!";
1209
    constexpr const char* data() const { return message;}
1210
    constexpr unsigned size() const
1211
    {
1212
        return ::size(message);
1213
    }
1214
};
1215
Message message;
1216
static_assert(true, message);
1217
)cpp";
1218
  std::string fileName = "main.cpp";
1219
  WriteFile(fileName, testSource);
1220
  const char *Args[] = {"-xc++", "-std=c++26"};
1221
  ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args,
1222
                                       std::size(Args), nullptr, 0, TUFlags);
1223
  ASSERT_EQ(clang_getNumDiagnostics(ClangTU), 0u);
1224
  std::optional<CXCursor> staticAssertCsr;
1225
  Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1226
    if (cursor.kind == CXCursor_StaticAssert) {
1227
      staticAssertCsr.emplace(cursor);
1228
    }
1229
    return CXChildVisit_Continue;
1230
  });
1231
  ASSERT_TRUE(staticAssertCsr.has_value());
1232
  int argCnt = 0;
1233
  Traverse(*staticAssertCsr, [&argCnt](CXCursor cursor, CXCursor parent) {
1234
    switch (argCnt) {
1235
    case 0:
1236
      EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr);
1237
      break;
1238
    case 1:
1239
      EXPECT_EQ(cursor.kind, CXCursor_DeclRefExpr);
1240
      break;
1241
    }
1242
    ++argCnt;
1243
    return CXChildVisit_Continue;
1244
  });
1245
  ASSERT_EQ(argCnt, 2);
1246
  EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), "");
1247
}
1248

1249
TEST_F(LibclangParseTest, ExposesAnnotateArgs) {
1250
  const char testSource[] = R"cpp(
1251
[[clang::annotate("category", 42)]]
1252
void func() {}
1253
)cpp";
1254
  std::string fileName = "main.cpp";
1255
  WriteFile(fileName, testSource);
1256

1257
  const char *Args[] = {"-xc++"};
1258
  ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
1259
                                       nullptr, 0, TUFlags);
1260

1261
  int attrCount = 0;
1262

1263
  Traverse(
1264
      [&attrCount](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
1265
        if (cursor.kind == CXCursor_AnnotateAttr) {
1266
          int childCount = 0;
1267
          clang_visitChildren(
1268
              cursor,
1269
              [](CXCursor child, CXCursor,
1270
                 CXClientData data) -> CXChildVisitResult {
1271
                int *pcount = static_cast<int *>(data);
1272

1273
                // we only expect one argument here, so bail otherwise
1274
                EXPECT_EQ(*pcount, 0);
1275

1276
                auto *result = clang_Cursor_Evaluate(child);
1277
                EXPECT_NE(result, nullptr);
1278
                EXPECT_EQ(clang_EvalResult_getAsInt(result), 42);
1279
                clang_EvalResult_dispose(result);
1280

1281
                ++*pcount;
1282

1283
                return CXChildVisit_Recurse;
1284
              },
1285
              &childCount);
1286
          attrCount++;
1287
          return CXChildVisit_Continue;
1288
        }
1289
        return CXChildVisit_Recurse;
1290
      });
1291

1292
  EXPECT_EQ(attrCount, 1);
1293
}
1294

1295
TEST_F(LibclangParseTest, clang_getSpellingLocation) {
1296
  std::string fileName = "main.c";
1297
  WriteFile(fileName, "#define X(value) int x = value;\nX(42)\n");
1298

1299
  ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), nullptr, 0,
1300
                                       nullptr, 0, TUFlags);
1301

1302
  int declarationCount = 0;
1303
  Traverse([&declarationCount](CXCursor cursor,
1304
                               CXCursor parent) -> CXChildVisitResult {
1305
    if (cursor.kind == CXCursor_VarDecl) {
1306
      declarationCount++;
1307

1308
      CXSourceLocation cxl = clang_getCursorLocation(cursor);
1309
      unsigned 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.
1314
      clang_getFileLocation(cxl, nullptr, &line, nullptr, nullptr);
1315
      EXPECT_EQ(line, 2U);
1316
      clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
1317
      EXPECT_EQ(line, 1U);
1318
    }
1319

1320
    return CXChildVisit_Recurse;
1321
  });
1322

1323
  EXPECT_EQ(declarationCount, 1);
1324
}
1325

1326
class LibclangRewriteTest : public LibclangParseTest {
1327
public:
1328
  CXRewriter Rew = nullptr;
1329
  std::string Filename;
1330
  CXFile File = nullptr;
1331

1332
  void SetUp() override {
1333
    LibclangParseTest::SetUp();
1334
    Filename = "file.cpp";
1335
    WriteFile(Filename, "int main() { return 0; }");
1336
    ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0,
1337
                                         nullptr, 0, TUFlags);
1338
    Rew = clang_CXRewriter_create(ClangTU);
1339
    File = clang_getFile(ClangTU, Filename.c_str());
1340
  }
1341
  void TearDown() override {
1342
    clang_CXRewriter_dispose(Rew);
1343
    LibclangParseTest::TearDown();
1344
  }
1345
};
1346

1347
static std::string getFileContent(const std::string& Filename) {
1348
  std::ifstream RewrittenFile(Filename);
1349
  std::string RewrittenFileContent;
1350
  std::string Line;
1351
  while (std::getline(RewrittenFile, Line)) {
1352
    if (RewrittenFileContent.empty())
1353
      RewrittenFileContent = Line;
1354
    else {
1355
      RewrittenFileContent += "\n" + Line;
1356
    }
1357
  }
1358
  return RewrittenFileContent;
1359
}
1360

1361
TEST_F(LibclangRewriteTest, RewriteReplace) {
1362
  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1363
  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1364
  CXSourceRange Rng	= clang_getRange(B, E);
1365

1366
  clang_CXRewriter_replaceText(Rew, Rng, "MAIN");
1367

1368
  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1369
  EXPECT_EQ(getFileContent(Filename), "int MAIN() { return 0; }");
1370
}
1371

1372
TEST_F(LibclangRewriteTest, RewriteReplaceShorter) {
1373
  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1374
  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1375
  CXSourceRange Rng	= clang_getRange(B, E);
1376

1377
  clang_CXRewriter_replaceText(Rew, Rng, "foo");
1378

1379
  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1380
  EXPECT_EQ(getFileContent(Filename), "int foo() { return 0; }");
1381
}
1382

1383
TEST_F(LibclangRewriteTest, RewriteReplaceLonger) {
1384
  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1385
  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1386
  CXSourceRange Rng	= clang_getRange(B, E);
1387

1388
  clang_CXRewriter_replaceText(Rew, Rng, "patatino");
1389

1390
  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1391
  EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }");
1392
}
1393

1394
TEST_F(LibclangRewriteTest, RewriteInsert) {
1395
  CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5);
1396

1397
  clang_CXRewriter_insertTextBefore(Rew, Loc, "ro");
1398

1399
  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1400
  EXPECT_EQ(getFileContent(Filename), "int romain() { return 0; }");
1401
}
1402

1403
TEST_F(LibclangRewriteTest, RewriteRemove) {
1404
  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
1405
  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
1406
  CXSourceRange Rng	= clang_getRange(B, E);
1407

1408
  clang_CXRewriter_removeText(Rew, Rng);
1409

1410
  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
1411
  EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
1412
}
1413

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.