clang-tools 22.0.0git
BackgroundIndexTests.cpp
Go to the documentation of this file.
1#include "Annotations.h"
2#include "CompileCommands.h"
3#include "Config.h"
4#include "Headers.h"
5#include "SyncAPI.h"
6#include "TestFS.h"
7#include "TestTU.h"
8#include "index/Background.h"
10#include "index/MemIndex.h"
11#include "clang/Tooling/ArgumentsAdjusters.h"
12#include "clang/Tooling/CompilationDatabase.h"
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/Support/ScopedPrinter.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17#include <deque>
18
19using ::testing::_;
20using ::testing::AllOf;
21using ::testing::Contains;
22using ::testing::ElementsAre;
23using ::testing::Not;
24using ::testing::Pair;
25using ::testing::UnorderedElementsAre;
26
27namespace clang {
28namespace clangd {
29
30MATCHER_P(named, N, "") { return arg.Name == N; }
31MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; }
32MATCHER(declared, "") {
33 return !StringRef(arg.CanonicalDeclaration.FileURI).empty();
34}
35MATCHER(defined, "") { return !StringRef(arg.Definition.FileURI).empty(); }
36MATCHER_P(fileURI, F, "") { return StringRef(arg.Location.FileURI) == F; }
37::testing::Matcher<const RefSlab &>
38refsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
39 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
40}
41// URI cannot be empty since it references keys in the IncludeGraph.
42MATCHER(emptyIncludeNode, "") {
43 return arg.Flags == IncludeGraphNode::SourceFlag::None && !arg.URI.empty() &&
44 arg.Digest == FileDigest{{0}} && arg.DirectIncludes.empty();
45}
46
47MATCHER(hadErrors, "") {
49}
50
51MATCHER_P(numReferences, N, "") { return arg.References == N; }
52
54 mutable std::mutex StorageMu;
55 llvm::StringMap<std::string> &Storage;
56 size_t &CacheHits;
57
58public:
59 MemoryShardStorage(llvm::StringMap<std::string> &Storage, size_t &CacheHits)
60 : Storage(Storage), CacheHits(CacheHits) {}
61 llvm::Error storeShard(llvm::StringRef ShardIdentifier,
62 IndexFileOut Shard) const override {
63 std::lock_guard<std::mutex> Lock(StorageMu);
64 AccessedPaths.insert(ShardIdentifier);
65 Storage[ShardIdentifier] = llvm::to_string(Shard);
66 return llvm::Error::success();
67 }
68 std::unique_ptr<IndexFileIn>
69 loadShard(llvm::StringRef ShardIdentifier) const override {
70 std::lock_guard<std::mutex> Lock(StorageMu);
71 AccessedPaths.insert(ShardIdentifier);
72 if (!Storage.contains(ShardIdentifier)) {
73 return nullptr;
74 }
75 auto IndexFile =
76 readIndexFile(Storage[ShardIdentifier], SymbolOrigin::Background);
77 if (!IndexFile) {
78 ADD_FAILURE() << "Error while reading " << ShardIdentifier << ':'
79 << IndexFile.takeError();
80 return nullptr;
81 }
82 CacheHits++;
83 return std::make_unique<IndexFileIn>(std::move(*IndexFile));
84 }
85
86 mutable llvm::StringSet<> AccessedPaths;
87};
88
89class BackgroundIndexTest : public ::testing::Test {
90protected:
92};
93
94TEST_F(BackgroundIndexTest, NoCrashOnErrorFile) {
95 MockFS FS;
96 FS.Files[testPath("root/A.cc")] = "error file";
97 llvm::StringMap<std::string> Storage;
98 size_t CacheHits = 0;
99 MemoryShardStorage MSS(Storage, CacheHits);
100 OverlayCDB CDB(/*Base=*/nullptr);
101 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
102 /*Opts=*/{});
103
104 tooling::CompileCommand Cmd;
105 Cmd.Filename = testPath("root/A.cc");
106 Cmd.Directory = testPath("root");
107 Cmd.CommandLine = {"clang++", "-DA=1", testPath("root/A.cc")};
108 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
109
110 ASSERT_TRUE(Idx.blockUntilIdleForTest());
111}
112
114 MockFS FS;
115 // Set up two identical TUs, foo and bar.
116 // They define foo::one and bar::one.
117 std::vector<tooling::CompileCommand> Cmds;
118 for (std::string Name : {"foo", "bar", "baz"}) {
119 std::string Filename = Name + ".cpp";
120 std::string Header = Name + ".h";
121 FS.Files[Filename] = "#include \"" + Header + "\"";
122 FS.Files[Header] = "namespace " + Name + " { int one; }";
123 tooling::CompileCommand Cmd;
124 Cmd.Filename = Filename;
125 Cmd.Directory = testRoot();
126 Cmd.CommandLine = {"clang++", Filename};
127 Cmds.push_back(std::move(Cmd));
128 }
129 // Context provider that installs a configuration mutating foo's command.
130 // This causes it to define foo::two instead of foo::one.
131 // It also disables indexing of baz entirely.
133 Opts.ContextProvider = [](PathRef P) {
134 Config C;
135 if (P.ends_with("foo.cpp"))
136 C.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) {
137 Argv = tooling::getInsertArgumentAdjuster("-Done=two")(Argv, "");
138 });
139 if (P.ends_with("baz.cpp"))
140 C.Index.Background = Config::BackgroundPolicy::Skip;
141 return Context::current().derive(Config::Key, std::move(C));
142 };
143 // Create the background index.
144 llvm::StringMap<std::string> Storage;
145 size_t CacheHits = 0;
146 MemoryShardStorage MSS(Storage, CacheHits);
147 // We need the CommandMangler, because that applies the config we're testing.
148 OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{},
150
151 BackgroundIndex Idx(
152 FS, CDB, [&](llvm::StringRef) { return &MSS; }, std::move(Opts));
153 // Index the two files.
154 for (auto &Cmd : Cmds) {
155 std::string FullPath = testPath(Cmd.Filename);
156 CDB.setCompileCommand(FullPath, std::move(Cmd));
157 }
158 // Wait for both files to be indexed.
159 ASSERT_TRUE(Idx.blockUntilIdleForTest());
160 EXPECT_THAT(runFuzzyFind(Idx, ""),
161 UnorderedElementsAre(qName("foo"), qName("foo::two"),
162 qName("bar"), qName("bar::one")));
163}
164
166 MockFS FS;
167 // a.h yields different symbols when included by A.cc vs B.cc.
168 FS.Files[testPath("root/A.h")] = R"cpp(
169 void common();
170 void f_b();
171 #if A
172 class A_CC {};
173 #else
174 class B_CC{};
175 #endif
176 )cpp";
177 FS.Files[testPath("root/A.cc")] =
178 "#include \"A.h\"\nstatic void g() { (void)common; }";
179 FS.Files[testPath("root/B.cc")] =
180 R"cpp(
181 #define A 0
182 #include "A.h"
183 void f_b() {
184 (void)common;
185 (void)common;
186 (void)common;
187 (void)common;
188 })cpp";
189 llvm::StringMap<std::string> Storage;
190 size_t CacheHits = 0;
191 MemoryShardStorage MSS(Storage, CacheHits);
192 OverlayCDB CDB(/*Base=*/nullptr);
194 BackgroundIndex Idx(
195 FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
196
197 tooling::CompileCommand Cmd;
198 Cmd.Filename = testPath("root/A.cc");
199 Cmd.Directory = testPath("root");
200 Cmd.CommandLine = {"clang++", "-DA=1", testPath("root/A.cc")};
201 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
202
203 ASSERT_TRUE(Idx.blockUntilIdleForTest());
204 EXPECT_THAT(runFuzzyFind(Idx, ""),
205 UnorderedElementsAre(AllOf(named("common"), numReferences(1U)),
206 AllOf(named("A_CC"), numReferences(0U)),
207 AllOf(named("g"), numReferences(1U)),
208 AllOf(named("f_b"), declared(),
209 Not(defined()), numReferences(0U))));
210
211 Cmd.Filename = testPath("root/B.cc");
212 Cmd.CommandLine = {"clang++", Cmd.Filename};
213 CDB.setCompileCommand(testPath("root/B.cc"), Cmd);
214
215 ASSERT_TRUE(Idx.blockUntilIdleForTest());
216 // B_CC is dropped as we don't collect symbols from A.h in this compilation.
217 EXPECT_THAT(runFuzzyFind(Idx, ""),
218 UnorderedElementsAre(AllOf(named("common"), numReferences(5U)),
219 AllOf(named("A_CC"), numReferences(0U)),
220 AllOf(named("g"), numReferences(1U)),
221 AllOf(named("f_b"), declared(), defined(),
222 numReferences(1U))));
223
224 auto Syms = runFuzzyFind(Idx, "common");
225 EXPECT_THAT(Syms, UnorderedElementsAre(named("common")));
226 auto Common = *Syms.begin();
227 EXPECT_THAT(getRefs(Idx, Common.ID),
228 refsAre({fileURI("unittest:///root/A.h"),
229 fileURI("unittest:///root/A.cc"),
230 fileURI("unittest:///root/B.cc"),
231 fileURI("unittest:///root/B.cc"),
232 fileURI("unittest:///root/B.cc"),
233 fileURI("unittest:///root/B.cc")}));
234}
235
236TEST_F(BackgroundIndexTest, ConstructorForwarding) {
237 Annotations Header(R"cpp(
238 namespace std {
239 template <class T> T &&forward(T &t);
240 template <class T, class... Args> T *make_unique(Args &&...args) {
241 return new T(std::forward<Args>(args)...);
242 }
243 }
244 struct Test {
245 [[Test]](){}
246 };
247 )cpp");
248 Annotations Main(R"cpp(
249 #include "header.hpp"
250 int main() {
251 auto a = std::[[make_unique]]<Test>();
252 }
253 )cpp");
255 MockFS FS;
256 llvm::StringMap<std::string> Storage;
257 size_t CacheHits = 0;
258 MemoryShardStorage MSS(Storage, CacheHits);
259 OverlayCDB CDB(/*Base=*/nullptr);
261 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
262
263 FS.Files[testPath("root/header.hpp")] = Header.code();
264 FS.Files[testPath("root/test.cpp")] = Main.code();
265
266 tooling::CompileCommand Cmd;
267 Cmd.Filename = testPath("root/test.cpp");
268 Cmd.Directory = testPath("root");
269 Cmd.CommandLine = {"clang++", testPath("root/test.cpp")};
270 CDB.setCompileCommand(testPath("root/test.cpp"), Cmd);
271
272 ASSERT_TRUE(Idx.blockUntilIdleForTest());
273
274 auto Syms = runFuzzyFind(Idx, "Test");
275 auto Constructor =
276 std::find_if(Syms.begin(), Syms.end(), [](const Symbol &S) {
277 return S.SymInfo.Kind == index::SymbolKind::Constructor;
278 });
279 ASSERT_TRUE(Constructor != Syms.end());
280 EXPECT_THAT(getRefs(Idx, Constructor->ID),
281 refsAre({fileURI("unittest:///root/header.hpp"),
282 fileURI("unittest:///root/test.cpp")}));
283}
284
285TEST_F(BackgroundIndexTest, ConstructorForwardingMultiFile) {
286 // If a forwarding function like `make_unique` is defined in a header its body
287 // used to be skipped on the second encounter. This meant in practise we could
288 // only find constructors indirectly called by these type of functions in the
289 // first indexed file (and all files that were indexed at the same time,
290 // before a flag to skip it was set).
291 Annotations Header(R"cpp(
292 namespace std {
293 template <class T> T &&forward(T &t);
294 template <class T, class... Args> T *make_unique(Args &&...args) {
295 return new T(std::forward<Args>(args)...);
296 }
297 }
298 struct Test {
299 [[Test]](){}
300 };
301 )cpp");
302 Annotations First(R"cpp(
303 #include "header.hpp"
304 int main() {
305 auto a = std::[[make_unique]]<Test>();
306 }
307 )cpp");
308 Annotations Second(R"cpp(
309 #include "header.hpp"
310 void test() {
311 auto a = std::[[make_unique]]<Test>();
312 }
313 )cpp");
314
315 MockFS FS;
316 llvm::StringMap<std::string> Storage;
317 size_t CacheHits = 0;
318 MemoryShardStorage MSS(Storage, CacheHits);
319 OverlayCDB CDB(/*Base=*/nullptr);
321 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
322
323 FS.Files[testPath("root/header.hpp")] = Header.code();
324 FS.Files[testPath("root/first.cpp")] = First.code();
325 FS.Files[testPath("root/second.cpp")] = Second.code();
326
327 tooling::CompileCommand Cmd;
328 Cmd.Filename = testPath("root/first.cpp");
329 Cmd.Directory = testPath("root");
330 Cmd.CommandLine = {"clang++", testPath("root/first.cpp")};
331 CDB.setCompileCommand(testPath("root/first.cpp"), Cmd);
333 // Make sure the first file is done indexing to make sure the flag for the
334 // header is set.
335 ASSERT_TRUE(Idx.blockUntilIdleForTest());
336
337 Cmd.Filename = testPath("root/second.cpp");
338 Cmd.Directory = testPath("root");
339 Cmd.CommandLine = {"clang++", testPath("root/second.cpp")};
340 CDB.setCompileCommand(testPath("root/second.cpp"), Cmd);
341
342 ASSERT_TRUE(Idx.blockUntilIdleForTest());
343
344 auto Syms = runFuzzyFind(Idx, "Test");
345 auto Constructor =
346 std::find_if(Syms.begin(), Syms.end(), [](const Symbol &S) {
347 return S.SymInfo.Kind == index::SymbolKind::Constructor;
348 });
349 ASSERT_TRUE(Constructor != Syms.end());
350 EXPECT_THAT(getRefs(Idx, Constructor->ID),
351 refsAre({fileURI("unittest:///root/header.hpp"),
352 fileURI("unittest:///root/first.cpp"),
353 fileURI("unittest:///root/second.cpp")}));
354}
355
356TEST_F(BackgroundIndexTest, MainFileRefs) {
357 MockFS FS;
358 FS.Files[testPath("root/A.h")] = R"cpp(
359 void header_sym();
360 )cpp";
361 FS.Files[testPath("root/A.cc")] =
362 "#include \"A.h\"\nstatic void main_sym() { (void)header_sym; }";
363
364 llvm::StringMap<std::string> Storage;
365 size_t CacheHits = 0;
366 MemoryShardStorage MSS(Storage, CacheHits);
367 OverlayCDB CDB(/*Base=*/nullptr);
369 BackgroundIndex Idx(
370 FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
371
372 tooling::CompileCommand Cmd;
373 Cmd.Filename = testPath("root/A.cc");
374 Cmd.Directory = testPath("root");
375 Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
376 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
377
378 ASSERT_TRUE(Idx.blockUntilIdleForTest());
379 EXPECT_THAT(
380 runFuzzyFind(Idx, ""),
381 UnorderedElementsAre(AllOf(named("header_sym"), numReferences(1U)),
382 AllOf(named("main_sym"), numReferences(1U))));
383}
384
385TEST_F(BackgroundIndexTest, ShardStorageTest) {
386 MockFS FS;
387 FS.Files[testPath("root/A.h")] = R"cpp(
388 void common();
389 void f_b();
390 class A_CC {};
391 )cpp";
392 FS.Files[testPath("root/A.cc")] = R"cpp(
393 #include "A.h"
394 void g() { (void)common; }
395 class B_CC : public A_CC {};
396 )cpp";
397
398 llvm::StringMap<std::string> Storage;
399 size_t CacheHits = 0;
400 MemoryShardStorage MSS(Storage, CacheHits);
401
402 tooling::CompileCommand Cmd;
403 Cmd.Filename = testPath("root/A.cc");
404 Cmd.Directory = testPath("root");
405 Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
406 // Check nothing is loaded from Storage, but A.cc and A.h has been stored.
407 {
408 OverlayCDB CDB(/*Base=*/nullptr);
409 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
410 /*Opts=*/{});
411 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
412 ASSERT_TRUE(Idx.blockUntilIdleForTest());
413 }
414 EXPECT_EQ(CacheHits, 0U);
415 EXPECT_EQ(Storage.size(), 2U);
416
417 {
418 OverlayCDB CDB(/*Base=*/nullptr);
419 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
420 /*Opts=*/{});
421 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
422 ASSERT_TRUE(Idx.blockUntilIdleForTest());
423 }
424 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache.
425 EXPECT_EQ(Storage.size(), 2U);
426
427 auto ShardHeader = MSS.loadShard(testPath("root/A.h"));
428 EXPECT_NE(ShardHeader, nullptr);
429 EXPECT_THAT(
430 *ShardHeader->Symbols,
431 UnorderedElementsAre(named("common"), named("A_CC"),
432 AllOf(named("f_b"), declared(), Not(defined()))));
433 for (const auto &Ref : *ShardHeader->Refs)
434 EXPECT_THAT(Ref.second,
435 UnorderedElementsAre(fileURI("unittest:///root/A.h")));
436
437 auto ShardSource = MSS.loadShard(testPath("root/A.cc"));
438 EXPECT_NE(ShardSource, nullptr);
439 EXPECT_THAT(*ShardSource->Symbols,
440 UnorderedElementsAre(named("g"), named("B_CC")));
441 for (const auto &Ref : *ShardSource->Refs)
442 EXPECT_THAT(Ref.second,
443 UnorderedElementsAre(fileURI("unittest:///root/A.cc")));
444
445 // The BaseOf relationship between A_CC and B_CC is stored in both the file
446 // containing the definition of the subject (A_CC) and the file containing
447 // the definition of the object (B_CC).
448 SymbolID A = findSymbol(*ShardHeader->Symbols, "A_CC").ID;
449 SymbolID B = findSymbol(*ShardSource->Symbols, "B_CC").ID;
450 EXPECT_THAT(*ShardHeader->Relations,
451 UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B}));
452 EXPECT_THAT(*ShardSource->Relations,
453 UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B}));
454}
455
456TEST_F(BackgroundIndexTest, DirectIncludesTest) {
457 MockFS FS;
458 FS.Files[testPath("root/B.h")] = "";
459 FS.Files[testPath("root/A.h")] = R"cpp(
460 #include "B.h"
461 void common();
462 void f_b();
463 class A_CC {};
464 )cpp";
465 FS.Files[testPath("root/A.cc")] =
466 "#include \"A.h\"\nvoid g() { (void)common; }";
467
468 llvm::StringMap<std::string> Storage;
469 size_t CacheHits = 0;
470 MemoryShardStorage MSS(Storage, CacheHits);
471
472 tooling::CompileCommand Cmd;
473 Cmd.Filename = testPath("root/A.cc");
474 Cmd.Directory = testPath("root");
475 Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
476 {
477 OverlayCDB CDB(/*Base=*/nullptr);
478 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
479 /*Opts=*/{});
480 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
481 ASSERT_TRUE(Idx.blockUntilIdleForTest());
482 }
483
484 auto ShardSource = MSS.loadShard(testPath("root/A.cc"));
485 EXPECT_TRUE(ShardSource->Sources);
486 EXPECT_EQ(ShardSource->Sources->size(), 2U); // A.cc, A.h
487 EXPECT_THAT(
488 ShardSource->Sources->lookup("unittest:///root/A.cc").DirectIncludes,
489 UnorderedElementsAre("unittest:///root/A.h"));
490 EXPECT_NE(ShardSource->Sources->lookup("unittest:///root/A.cc").Digest,
491 FileDigest{{0}});
492 EXPECT_THAT(ShardSource->Sources->lookup("unittest:///root/A.h"),
493 emptyIncludeNode());
494
495 auto ShardHeader = MSS.loadShard(testPath("root/A.h"));
496 EXPECT_TRUE(ShardHeader->Sources);
497 EXPECT_EQ(ShardHeader->Sources->size(), 2U); // A.h, B.h
498 EXPECT_THAT(
499 ShardHeader->Sources->lookup("unittest:///root/A.h").DirectIncludes,
500 UnorderedElementsAre("unittest:///root/B.h"));
501 EXPECT_NE(ShardHeader->Sources->lookup("unittest:///root/A.h").Digest,
502 FileDigest{{0}});
503 EXPECT_THAT(ShardHeader->Sources->lookup("unittest:///root/B.h"),
504 emptyIncludeNode());
505}
506
507TEST_F(BackgroundIndexTest, ShardStorageLoad) {
508 MockFS FS;
509 FS.Files[testPath("root/A.h")] = R"cpp(
510 void common();
511 void f_b();
512 class A_CC {};
513 )cpp";
514 FS.Files[testPath("root/A.cc")] =
515 "#include \"A.h\"\nvoid g() { (void)common; }";
516
517 llvm::StringMap<std::string> Storage;
518 size_t CacheHits = 0;
519 MemoryShardStorage MSS(Storage, CacheHits);
520
521 tooling::CompileCommand Cmd;
522 Cmd.Filename = testPath("root/A.cc");
523 Cmd.Directory = testPath("root");
524 Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
525 // Check nothing is loaded from Storage, but A.cc and A.h has been stored.
526 {
527 OverlayCDB CDB(/*Base=*/nullptr);
528 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
529 /*Opts=*/{});
530 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
531 ASSERT_TRUE(Idx.blockUntilIdleForTest());
532 }
533
534 // Change header.
535 FS.Files[testPath("root/A.h")] = R"cpp(
536 void common();
537 void f_b();
538 class A_CC {};
539 class A_CCnew {};
540 )cpp";
541 {
542 OverlayCDB CDB(/*Base=*/nullptr);
543 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
544 /*Opts=*/{});
545 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
546 ASSERT_TRUE(Idx.blockUntilIdleForTest());
547 }
548 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache.
549
550 // Check if the new symbol has arrived.
551 auto ShardHeader = MSS.loadShard(testPath("root/A.h"));
552 EXPECT_NE(ShardHeader, nullptr);
553 EXPECT_THAT(*ShardHeader->Symbols, Contains(named("A_CCnew")));
554
555 // Change source.
556 FS.Files[testPath("root/A.cc")] =
557 "#include \"A.h\"\nvoid g() { (void)common; }\nvoid f_b() {}";
558 {
559 CacheHits = 0;
560 OverlayCDB CDB(/*Base=*/nullptr);
561 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
562 /*Opts=*/{});
563 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
564 ASSERT_TRUE(Idx.blockUntilIdleForTest());
565 }
566 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache.
567
568 // Check if the new symbol has arrived.
569 ShardHeader = MSS.loadShard(testPath("root/A.h"));
570 EXPECT_NE(ShardHeader, nullptr);
571 EXPECT_THAT(*ShardHeader->Symbols, Contains(named("A_CCnew")));
572 auto ShardSource = MSS.loadShard(testPath("root/A.cc"));
573 EXPECT_NE(ShardSource, nullptr);
574 EXPECT_THAT(*ShardSource->Symbols,
575 Contains(AllOf(named("f_b"), declared(), defined())));
576}
577
578TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) {
579 MockFS FS;
580 FS.Files[testPath("root/A.h")] = R"cpp(
581 void common();
582 void f_b();
583 class A_CC {};
584 )cpp";
585 FS.Files[testPath("root/B.h")] = R"cpp(
586 #include "A.h"
587 )cpp";
588 FS.Files[testPath("root/A.cc")] =
589 "#include \"B.h\"\nvoid g() { (void)common; }";
590
591 llvm::StringMap<std::string> Storage;
592 size_t CacheHits = 0;
593 MemoryShardStorage MSS(Storage, CacheHits);
594
595 tooling::CompileCommand Cmd;
596 Cmd.Filename = testPath("root/A.cc");
597 Cmd.Directory = testPath("root");
598 Cmd.CommandLine = {"clang++", testPath("root/A.cc")};
599 // Check that A.cc, A.h and B.h has been stored.
600 {
601 OverlayCDB CDB(/*Base=*/nullptr);
602 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
603 /*Opts=*/{});
604 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
605 ASSERT_TRUE(Idx.blockUntilIdleForTest());
606 }
607 EXPECT_THAT(Storage.keys(),
608 UnorderedElementsAre(testPath("root/A.cc"), testPath("root/A.h"),
609 testPath("root/B.h")));
610 auto ShardHeader = MSS.loadShard(testPath("root/B.h"));
611 EXPECT_NE(ShardHeader, nullptr);
612 EXPECT_TRUE(ShardHeader->Symbols->empty());
613
614 // Check that A.cc, A.h and B.h has been loaded.
615 {
616 CacheHits = 0;
617 OverlayCDB CDB(/*Base=*/nullptr);
618 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
619 /*Opts=*/{});
620 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
621 ASSERT_TRUE(Idx.blockUntilIdleForTest());
622 }
623 EXPECT_EQ(CacheHits, 3U);
624
625 // Update B.h to contain some symbols.
626 FS.Files[testPath("root/B.h")] = R"cpp(
627 #include "A.h"
628 void new_func();
629 )cpp";
630 // Check that B.h has been stored with new contents.
631 {
632 CacheHits = 0;
633 OverlayCDB CDB(/*Base=*/nullptr);
634 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
635 /*Opts=*/{});
636 CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
637 ASSERT_TRUE(Idx.blockUntilIdleForTest());
638 }
639 EXPECT_EQ(CacheHits, 3U);
640 ShardHeader = MSS.loadShard(testPath("root/B.h"));
641 EXPECT_NE(ShardHeader, nullptr);
642 EXPECT_THAT(*ShardHeader->Symbols,
643 Contains(AllOf(named("new_func"), declared(), Not(defined()))));
644}
645
646TEST_F(BackgroundIndexTest, NoDotsInAbsPath) {
647 MockFS FS;
648 llvm::StringMap<std::string> Storage;
649 size_t CacheHits = 0;
650 MemoryShardStorage MSS(Storage, CacheHits);
651 OverlayCDB CDB(/*Base=*/nullptr);
652 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
653 /*Opts=*/{});
654 ASSERT_TRUE(Idx.blockUntilIdleForTest());
655
656 tooling::CompileCommand Cmd;
657 FS.Files[testPath("root/A.cc")] = "";
658 Cmd.Filename = "../A.cc";
659 Cmd.Directory = testPath("root/build");
660 Cmd.CommandLine = {"clang++", "../A.cc"};
661 CDB.setCompileCommand(testPath("root/build/../A.cc"), Cmd);
662 ASSERT_TRUE(Idx.blockUntilIdleForTest());
663
664 FS.Files[testPath("root/B.cc")] = "";
665 Cmd.Filename = "./B.cc";
666 Cmd.Directory = testPath("root");
667 Cmd.CommandLine = {"clang++", "./B.cc"};
668 CDB.setCompileCommand(testPath("root/./B.cc"), Cmd);
669 ASSERT_TRUE(Idx.blockUntilIdleForTest());
670
671 for (llvm::StringRef AbsPath : MSS.AccessedPaths.keys()) {
672 EXPECT_FALSE(AbsPath.contains("./")) << AbsPath;
673 EXPECT_FALSE(AbsPath.contains("../")) << AbsPath;
674 }
675}
676
677TEST_F(BackgroundIndexTest, UncompilableFiles) {
678 MockFS FS;
679 llvm::StringMap<std::string> Storage;
680 size_t CacheHits = 0;
681 MemoryShardStorage MSS(Storage, CacheHits);
682 OverlayCDB CDB(/*Base=*/nullptr);
683 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
684 /*Opts=*/{});
685
686 tooling::CompileCommand Cmd;
687 FS.Files[testPath("A.h")] = "void foo();";
688 FS.Files[testPath("B.h")] = "#include \"C.h\"\nasdf;";
689 FS.Files[testPath("C.h")] = "";
690 FS.Files[testPath("A.cc")] = R"cpp(
691 #include "A.h"
692 #include "B.h"
693 #include "not_found_header.h"
694
695 void foo() {}
696 )cpp";
697 Cmd.Filename = "../A.cc";
698 Cmd.Directory = testPath("build");
699 Cmd.CommandLine = {"clang++", "../A.cc"};
700 CDB.setCompileCommand(testPath("build/../A.cc"), Cmd);
701 ASSERT_TRUE(Idx.blockUntilIdleForTest());
702
703 EXPECT_THAT(Storage.keys(),
704 UnorderedElementsAre(testPath("A.cc"), testPath("A.h"),
705 testPath("B.h"), testPath("C.h")));
706
707 {
708 auto Shard = MSS.loadShard(testPath("A.cc"));
709 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("foo")));
710 EXPECT_THAT(Shard->Sources->keys(),
711 UnorderedElementsAre("unittest:///A.cc", "unittest:///A.h",
712 "unittest:///B.h"));
713 EXPECT_THAT(Shard->Sources->lookup("unittest:///A.cc"), hadErrors());
714 }
716 {
717 auto Shard = MSS.loadShard(testPath("A.h"));
718 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("foo")));
719 EXPECT_THAT(Shard->Sources->keys(),
720 UnorderedElementsAre("unittest:///A.h"));
721 EXPECT_THAT(Shard->Sources->lookup("unittest:///A.h"), hadErrors());
722 }
723
724 {
725 auto Shard = MSS.loadShard(testPath("B.h"));
726 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("asdf")));
727 EXPECT_THAT(Shard->Sources->keys(),
728 UnorderedElementsAre("unittest:///B.h", "unittest:///C.h"));
729 EXPECT_THAT(Shard->Sources->lookup("unittest:///B.h"), hadErrors());
730 }
731
732 {
733 auto Shard = MSS.loadShard(testPath("C.h"));
734 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre());
735 EXPECT_THAT(Shard->Sources->keys(),
736 UnorderedElementsAre("unittest:///C.h"));
737 EXPECT_THAT(Shard->Sources->lookup("unittest:///C.h"), hadErrors());
738 }
739}
740
741TEST_F(BackgroundIndexTest, CmdLineHash) {
742 MockFS FS;
743 llvm::StringMap<std::string> Storage;
744 size_t CacheHits = 0;
745 MemoryShardStorage MSS(Storage, CacheHits);
746 OverlayCDB CDB(/*Base=*/nullptr);
747 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
748 /*Opts=*/{});
750 tooling::CompileCommand Cmd;
751 FS.Files[testPath("A.cc")] = "#include \"A.h\"";
752 FS.Files[testPath("A.h")] = "";
753 Cmd.Filename = "../A.cc";
754 Cmd.Directory = testPath("build");
755 Cmd.CommandLine = {"clang++", "../A.cc", "-fsyntax-only"};
756 CDB.setCompileCommand(testPath("build/../A.cc"), Cmd);
757 ASSERT_TRUE(Idx.blockUntilIdleForTest());
758
759 EXPECT_THAT(Storage.keys(),
760 UnorderedElementsAre(testPath("A.cc"), testPath("A.h")));
761 // Make sure we only store the Cmd for main file.
762 EXPECT_FALSE(MSS.loadShard(testPath("A.h"))->Cmd);
763
764 tooling::CompileCommand CmdStored = *MSS.loadShard(testPath("A.cc"))->Cmd;
765 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine);
766 EXPECT_EQ(CmdStored.Directory, Cmd.Directory);
767}
768
769TEST_F(BackgroundIndexTest, Reindex) {
770 MockFS FS;
771 llvm::StringMap<std::string> Storage;
772 size_t CacheHits = 0;
773 MemoryShardStorage MSS(Storage, CacheHits);
774 OverlayCDB CDB(/*Base=*/nullptr);
775 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; },
776 /*Opts=*/{});
777
778 // Index a file.
779 FS.Files[testPath("A.cc")] = "int theOldFunction();";
780 tooling::CompileCommand Cmd;
781 Cmd.Filename = "../A.cc";
782 Cmd.Directory = testPath("build");
783 Cmd.CommandLine = {"clang++", "../A.cc", "-fsyntax-only"};
784 CDB.setCompileCommand(testPath("A.cc"), Cmd);
785 ASSERT_TRUE(Idx.blockUntilIdleForTest());
786
787 // Verify the result is indexed and stored.
788 EXPECT_EQ(1u, runFuzzyFind(Idx, "theOldFunction").size());
789 EXPECT_EQ(0u, runFuzzyFind(Idx, "theNewFunction").size());
790 std::string OldShard = Storage.lookup(testPath("A.cc"));
791 EXPECT_NE("", OldShard);
792
793 // Change the content and command, and notify to reindex it.
794 Cmd.CommandLine.push_back("-DFOO");
795 FS.Files[testPath("A.cc")] = "int theNewFunction();";
796 CDB.setCompileCommand(testPath("A.cc"), Cmd);
797 ASSERT_TRUE(Idx.blockUntilIdleForTest());
798
799 // Currently, we will never index the same main file again.
800 EXPECT_EQ(1u, runFuzzyFind(Idx, "theOldFunction").size());
801 EXPECT_EQ(0u, runFuzzyFind(Idx, "theNewFunction").size());
802 EXPECT_EQ(OldShard, Storage.lookup(testPath("A.cc")));
803}
804
805class BackgroundIndexRebuilderTest : public testing::Test {
806protected:
807 BackgroundIndexRebuilderTest()
808 : Source(IndexContents::All, /*SupportContainedRefs=*/true),
809 Target(std::make_unique<MemIndex>()),
810 Rebuilder(&Target, &Source, /*Threads=*/10) {
811 // Prepare FileSymbols with TestSymbol in it, for checkRebuild.
812 TestSymbol.ID = SymbolID("foo");
813 }
814
815 // Perform Action and determine whether it rebuilt the index or not.
816 bool checkRebuild(std::function<void()> Action) {
817 // Update name so we can tell if the index updates.
818 VersionStorage.push_back("Sym" + std::to_string(++VersionCounter));
819 TestSymbol.Name = VersionStorage.back();
820 SymbolSlab::Builder SB;
821 SB.insert(TestSymbol);
822 Source.update("", std::make_unique<SymbolSlab>(std::move(SB).build()),
823 nullptr, nullptr, false);
824 // Now maybe update the index.
825 Action();
826 // Now query the index to get the name count.
827 std::string ReadName;
828 LookupRequest Req;
829 Req.IDs.insert(TestSymbol.ID);
830 Target.lookup(Req,
831 [&](const Symbol &S) { ReadName = std::string(S.Name); });
832 // The index was rebuild if the name is up to date.
833 return ReadName == VersionStorage.back();
834 }
835
836 Symbol TestSymbol;
837 FileSymbols Source;
838 SwapIndex Target;
839 BackgroundIndexRebuilder Rebuilder;
840
841 unsigned VersionCounter = 0;
842 std::deque<std::string> VersionStorage;
843};
844
845TEST_F(BackgroundIndexRebuilderTest, IndexingTUs) {
846 for (unsigned I = 0; I < Rebuilder.TUsBeforeFirstBuild - 1; ++I)
847 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
848 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
849 for (unsigned I = 0; I < Rebuilder.TUsBeforeRebuild - 1; ++I)
850 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
851 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
852}
853
854TEST_F(BackgroundIndexRebuilderTest, LoadingShards) {
855 Rebuilder.startLoading();
856 Rebuilder.loadedShard(10);
857 Rebuilder.loadedShard(20);
858 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
859
860 // No rebuild for no shards.
861 Rebuilder.startLoading();
862 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
863
864 // Loads can overlap.
865 Rebuilder.startLoading();
866 Rebuilder.loadedShard(1);
867 Rebuilder.startLoading();
868 Rebuilder.loadedShard(1);
869 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
870 Rebuilder.loadedShard(1);
871 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
872
873 // No rebuilding for indexed files while loading.
874 Rebuilder.startLoading();
875 for (unsigned I = 0; I < 3 * Rebuilder.TUsBeforeRebuild; ++I)
876 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
877 // But they get indexed when we're done, even if no shards were loaded.
878 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
879}
880
881TEST(BackgroundQueueTest, Priority) {
882 // Create high and low priority tasks.
883 // Once a bunch of high priority tasks have run, the queue is stopped.
884 // So the low priority tasks should never run.
886 std::atomic<unsigned> HiRan(0), LoRan(0);
887 BackgroundQueue::Task Lo([&] { ++LoRan; });
888 BackgroundQueue::Task Hi([&] {
889 if (++HiRan >= 10)
890 Q.stop();
891 });
892 Hi.QueuePri = 100;
893
894 // Enqueuing the low-priority ones first shouldn't make them run first.
895 Q.append(std::vector<BackgroundQueue::Task>(30, Lo));
896 for (unsigned I = 0; I < 30; ++I)
897 Q.push(Hi);
898
899 AsyncTaskRunner ThreadPool;
900 for (unsigned I = 0; I < 5; ++I)
901 ThreadPool.runAsync("worker", [&] { Q.work(); });
902 // We should test enqueue with active workers, but it's hard to avoid races.
903 // Just make sure we don't crash.
904 Q.push(Lo);
905 Q.append(std::vector<BackgroundQueue::Task>(2, Hi));
906
907 // After finishing, check the tasks that ran.
908 ThreadPool.wait();
909 EXPECT_GE(HiRan, 10u);
910 EXPECT_EQ(LoRan, 0u);
911}
912
913TEST(BackgroundQueueTest, Boost) {
914 std::string Sequence;
915
916 BackgroundQueue::Task A([&] { Sequence.push_back('A'); });
917 A.Tag = "A";
918 A.QueuePri = 1;
919
920 BackgroundQueue::Task B([&] { Sequence.push_back('B'); });
921 B.QueuePri = 2;
922 B.Tag = "B";
923
924 {
925 BackgroundQueue Q;
926 Q.append({A, B});
927 Q.work([&] { Q.stop(); });
928 EXPECT_EQ("BA", Sequence) << "priority order";
929 }
930 Sequence.clear();
933 Q.boost("A", 3);
934 Q.append({A, B});
935 Q.work([&] { Q.stop(); });
936 EXPECT_EQ("AB", Sequence) << "A was boosted before enqueueing";
937 }
938 Sequence.clear();
939 {
940 BackgroundQueue Q;
941 Q.append({A, B});
942 Q.boost("A", 3);
943 Q.work([&] { Q.stop(); });
944 EXPECT_EQ("AB", Sequence) << "A was boosted after enqueueing";
945 }
946}
947
948TEST(BackgroundQueueTest, Duplicates) {
949 std::string Sequence;
950 BackgroundQueue::Task A([&] { Sequence.push_back('A'); });
951 A.QueuePri = 100;
952 A.Key = 1;
953 BackgroundQueue::Task B([&] { Sequence.push_back('B'); });
954 // B has no key, and is not subject to duplicate detection.
955 B.QueuePri = 50;
956
957 BackgroundQueue Q;
958 Q.append({A, B, A, B}); // One A is dropped, the other is high priority.
959 Q.work(/*OnIdle=*/[&] {
960 // The first time we go idle, we enqueue the same task again.
961 if (!llvm::is_contained(Sequence, ' ')) {
962 Sequence.push_back(' ');
963 Q.append({A, B, A, B}); // Both As are dropped.
964 } else {
965 Q.stop();
966 }
967 });
968
969 // This could reasonably be "ABB BBA", if we had good *re*indexing support.
970 EXPECT_EQ("ABB BB", Sequence);
971}
972
973TEST(BackgroundQueueTest, Progress) {
974 using testing::AnyOf;
975 BackgroundQueue::Stats S;
976 BackgroundQueue Q([&](BackgroundQueue::Stats New) {
977 // Verify values are sane.
978 // Items are enqueued one at a time (at least in this test).
979 EXPECT_THAT(New.Enqueued, AnyOf(S.Enqueued, S.Enqueued + 1));
980 // Items are completed one at a time.
981 EXPECT_THAT(New.Completed, AnyOf(S.Completed, S.Completed + 1));
982 // Items are started or completed one at a time.
983 EXPECT_THAT(New.Active, AnyOf(S.Active - 1, S.Active, S.Active + 1));
984 // Idle point only advances in time.
985 EXPECT_GE(New.LastIdle, S.LastIdle);
986 // Idle point is a task that has been completed in the past.
987 EXPECT_LE(New.LastIdle, New.Completed);
988 // LastIdle is now only if we're really idle.
989 EXPECT_EQ(New.LastIdle == New.Enqueued,
990 New.Completed == New.Enqueued && New.Active == 0u);
991 S = New;
992 });
993
994 // Two types of tasks: a ping task enqueues a pong task.
995 // This avoids all enqueues followed by all completions (boring!)
996 std::atomic<int> PingCount(0), PongCount(0);
997 BackgroundQueue::Task Pong([&] { ++PongCount; });
998 BackgroundQueue::Task Ping([&] {
999 ++PingCount;
1000 Q.push(Pong);
1001 });
1002
1003 for (int I = 0; I < 1000; ++I)
1004 Q.push(Ping);
1005 // Spin up some workers and stop while idle.
1006 AsyncTaskRunner ThreadPool;
1007 for (unsigned I = 0; I < 5; ++I)
1008 ThreadPool.runAsync("worker", [&] { Q.work([&] { Q.stop(); }); });
1009 ThreadPool.wait();
1010
1011 // Everything's done, check final stats.
1012 // Assertions above ensure we got from 0 to 2000 in a reasonable way.
1013 EXPECT_EQ(PingCount.load(), 1000);
1014 EXPECT_EQ(PongCount.load(), 1000);
1015 EXPECT_EQ(S.Active, 0u);
1016 EXPECT_EQ(S.Enqueued, 2000u);
1017 EXPECT_EQ(S.Completed, 2000u);
1018 EXPECT_EQ(S.LastIdle, 2000u);
1019}
1020
1021TEST(BackgroundIndex, Profile) {
1022 MockFS FS;
1023 MockCompilationDatabase CDB;
1024 BackgroundIndex Idx(FS, CDB, [](llvm::StringRef) { return nullptr; },
1025 /*Opts=*/{});
1026
1027 llvm::BumpPtrAllocator Alloc;
1028 MemoryTree MT(&Alloc);
1029 Idx.profile(MT);
1030 ASSERT_THAT(MT.children(),
1031 UnorderedElementsAre(Pair("slabs", _), Pair("index", _)));
1032}
1033
1034} // namespace clangd
1035} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
void append(std::vector< Task >)
void work(std::function< void()> OnIdle=nullptr)
void boost(llvm::StringRef Tag, unsigned NewPriority)
Context derive(const Key< Type > &Key, std::decay_t< Type > Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
Definition Context.h:119
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition Context.cpp:27
llvm::Error storeShard(llvm::StringRef ShardIdentifier, IndexFileOut Shard) const override
std::unique_ptr< IndexFileIn > loadShard(llvm::StringRef ShardIdentifier) const override
MemoryShardStorage(llvm::StringMap< std::string > &Storage, size_t &CacheHits)
llvm::StringMap< std::string > Files
Definition TestFS.h:45
Wraps another compilation database, and supports overriding the commands using an in-memory mapping.
bool setCompileCommand(PathRef File, std::optional< tooling::CompileCommand > CompilationCommand)
Sets or clears the compilation command for a particular file.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
::testing::Matcher< const RefSlab & > refsAre(std::vector<::testing::Matcher< Ref > > Matchers)
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data, SymbolOrigin Origin)
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:93
TEST(BackgroundQueueTest, Priority)
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
Definition SyncAPI.cpp:139
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition TestTU.cpp:186
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
Definition SyncAPI.cpp:126
std::array< uint8_t, 8 > FileDigest
Definition SourceCode.h:42
const char * testRoot()
Definition TestFS.cpp:85
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::function< Context(PathRef)> ContextProvider
Definition Background.h:147
A work item on the thread pool's queue.
Definition Background.h:72
static CommandMangler forTests()
Settings that express user/project preferences and control clangd behavior.
Definition Config.h:44
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition Config.h:48
llvm::DenseSet< SymbolID > IDs
Definition Index.h:65
Represents a symbol occurrence in the source file.
Definition Ref.h:88
Represents a relation between two symbols.
Definition Relation.h:32
The class presents a C++ symbol, e.g.
Definition Symbol.h:39
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
Definition Symbol.h:45
SymbolID ID
The ID of the symbol.
Definition Symbol.h:41