clang-tools 20.0.0git
FileIndexTests.cpp
Go to the documentation of this file.
1//===-- FileIndexTests.cpp ---------------------------*- C++ -*-----------===//
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 "Annotations.h"
10#include "Compiler.h"
11#include "Headers.h"
12#include "ParsedAST.h"
13#include "SyncAPI.h"
14#include "TestFS.h"
15#include "TestTU.h"
16#include "TestWorkspace.h"
17#include "URI.h"
18#include "clang-include-cleaner/Record.h"
19#include "index/FileIndex.h"
20#include "index/Index.h"
21#include "index/Ref.h"
22#include "index/Relation.h"
23#include "index/Serialization.h"
24#include "index/Symbol.h"
25#include "index/SymbolID.h"
26#include "support/Threading.h"
27#include "clang/Frontend/CompilerInvocation.h"
28#include "clang/Tooling/CompilationDatabase.h"
29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/Support/Allocator.h"
31#include "gmock/gmock.h"
32#include "gtest/gtest.h"
33#include <memory>
34#include <utility>
35#include <vector>
36
37using ::testing::_;
38using ::testing::AllOf;
39using ::testing::Contains;
40using ::testing::ElementsAre;
41using ::testing::Gt;
42using ::testing::IsEmpty;
43using ::testing::Pair;
44using ::testing::UnorderedElementsAre;
45
46MATCHER_P(refRange, Range, "") {
47 return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(),
48 arg.Location.End.line(), arg.Location.End.column()) ==
49 std::make_tuple(Range.start.line, Range.start.character,
50 Range.end.line, Range.end.character);
51}
52MATCHER_P(fileURI, F, "") { return llvm::StringRef(arg.Location.FileURI) == F; }
53MATCHER_P(declURI, U, "") {
54 return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U;
55}
56MATCHER_P(defURI, U, "") {
57 return llvm::StringRef(arg.Definition.FileURI) == U;
58}
59MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; }
60MATCHER_P(numReferences, N, "") { return arg.References == N; }
61MATCHER_P(hasOrign, O, "") { return bool(arg.Origin & O); }
62
63MATCHER_P(includeHeader, P, "") {
64 return (arg.IncludeHeaders.size() == 1) &&
65 (arg.IncludeHeaders.begin()->IncludeHeader == P);
66}
67
68namespace clang {
69namespace clangd {
70namespace {
71::testing::Matcher<const RefSlab &>
72refsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
73 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
74}
75
76Symbol symbol(llvm::StringRef ID) {
77 Symbol Sym;
78 Sym.ID = SymbolID(ID);
79 Sym.Name = ID;
80 return Sym;
81}
82
83std::unique_ptr<SymbolSlab> numSlab(int Begin, int End) {
84 SymbolSlab::Builder Slab;
85 for (int I = Begin; I <= End; I++)
86 Slab.insert(symbol(std::to_string(I)));
87 return std::make_unique<SymbolSlab>(std::move(Slab).build());
88}
89
90std::unique_ptr<RefSlab> refSlab(const SymbolID &ID, const char *Path) {
91 RefSlab::Builder Slab;
92 Ref R;
93 R.Location.FileURI = Path;
94 R.Kind = RefKind::Reference;
95 Slab.insert(ID, R);
96 return std::make_unique<RefSlab>(std::move(Slab).build());
97}
98
99std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) {
100 RelationSlab::Builder RelBuilder;
101 for (auto &Rel : Rels)
102 RelBuilder.insert(Rel);
103 return std::make_unique<RelationSlab>(std::move(RelBuilder).build());
104}
105
106TEST(FileSymbolsTest, UpdateAndGet) {
107 FileSymbols FS(IndexContents::All, true);
108 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
109
110 FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr,
111 false);
112 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""),
113 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
114 EXPECT_THAT(getRefs(*FS.buildIndex(IndexType::Light), SymbolID("1")),
115 refsAre({fileURI("f1.cc")}));
116}
117
118TEST(FileSymbolsTest, Overlap) {
119 FileSymbols FS(IndexContents::All, true);
120 FS.update("f1", numSlab(1, 3), nullptr, nullptr, false);
121 FS.update("f2", numSlab(3, 5), nullptr, nullptr, false);
123 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""),
124 UnorderedElementsAre(qName("1"), qName("2"), qName("3"),
125 qName("4"), qName("5")));
126}
127
128TEST(FileSymbolsTest, MergeOverlap) {
129 FileSymbols FS(IndexContents::All, true);
130 auto OneSymboSlab = [](Symbol Sym) {
131 SymbolSlab::Builder S;
132 S.insert(Sym);
133 return std::make_unique<SymbolSlab>(std::move(S).build());
134 };
135 auto X1 = symbol("x");
136 X1.CanonicalDeclaration.FileURI = "file:///x1";
137 auto X2 = symbol("x");
138 X2.Definition.FileURI = "file:///x2";
139
140 FS.update("f1", OneSymboSlab(X1), nullptr, nullptr, false);
141 FS.update("f2", OneSymboSlab(X2), nullptr, nullptr, false);
143 EXPECT_THAT(
144 runFuzzyFind(*FS.buildIndex(Type, DuplicateHandling::Merge), "x"),
145 UnorderedElementsAre(
146 AllOf(qName("x"), declURI("file:///x1"), defURI("file:///x2"))));
147}
148
149TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
150 FileSymbols FS(IndexContents::All, true);
151
152 SymbolID ID("1");
153 FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
154
155 auto Symbols = FS.buildIndex(IndexType::Light);
156 EXPECT_THAT(runFuzzyFind(*Symbols, ""),
157 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
158 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
159
160 FS.update("f1", nullptr, nullptr, nullptr, false);
161 auto Empty = FS.buildIndex(IndexType::Light);
162 EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty());
163 EXPECT_THAT(getRefs(*Empty, ID), ElementsAre());
164
165 EXPECT_THAT(runFuzzyFind(*Symbols, ""),
166 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
167 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
168}
169
170// Adds Basename.cpp, which includes Basename.h, which contains Code.
171void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
172 TestTU File;
173 File.Filename = (Basename + ".cpp").str();
174 File.HeaderFilename = (Basename + ".h").str();
175 File.HeaderCode = std::string(Code);
176 auto AST = File.build();
177 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
178 AST.getASTContext(), AST.getPreprocessor(),
179 AST.getPragmaIncludes());
180}
181
182TEST(FileIndexTest, CustomizedURIScheme) {
183 FileIndex M(true);
184 update(M, "f", "class string {};");
185
186 EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
187}
188
189TEST(FileIndexTest, IndexAST) {
190 FileIndex M(true);
191 update(M, "f1", "namespace ns { void f() {} class X {}; }");
192
193 FuzzyFindRequest Req;
194 Req.Query = "";
195 Req.Scopes = {"ns::"};
196 EXPECT_THAT(runFuzzyFind(M, Req),
197 UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
198}
199
200TEST(FileIndexTest, NoLocal) {
201 FileIndex M(true);
202 update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
203
204 EXPECT_THAT(
205 runFuzzyFind(M, ""),
206 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
207}
208
209TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
210 FileIndex M(true);
211 update(M, "f1", "namespace ns { void f() {} class X {}; }");
212 update(M, "f2", "namespace ns { void ff() {} class X {}; }");
213
214 FuzzyFindRequest Req;
215 Req.Scopes = {"ns::"};
216 EXPECT_THAT(
217 runFuzzyFind(M, Req),
218 UnorderedElementsAre(qName("ns::f"), qName("ns::X"), qName("ns::ff")));
219}
220
221TEST(FileIndexTest, ClassMembers) {
222 FileIndex M(true);
223 update(M, "f1", "class X { static int m1; int m2; static void f(); };");
224
225 EXPECT_THAT(runFuzzyFind(M, ""),
226 UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"),
227 qName("X::f")));
228}
229
230TEST(FileIndexTest, IncludeCollected) {
231 FileIndex M(true);
232 update(
233 M, "f",
234 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
235
236 auto Symbols = runFuzzyFind(M, "");
237 EXPECT_THAT(Symbols, ElementsAre(_));
238 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
239 "<the/good/header.h>");
240}
241
242TEST(FileIndexTest, IWYUPragmaExport) {
243 FileIndex M(true);
244
245 TestTU File;
246 File.Code = R"cpp(#pragma once
247 #include "exporter.h"
248 )cpp";
249 File.HeaderFilename = "exporter.h";
250 File.HeaderCode = R"cpp(#pragma once
251 #include "private.h" // IWYU pragma: export
252 )cpp";
253 File.AdditionalFiles["private.h"] = "class Foo{};";
254 auto AST = File.build();
255 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
256 AST.getASTContext(), AST.getPreprocessor(),
257 AST.getPragmaIncludes());
258
259 auto Symbols = runFuzzyFind(M, "");
260 EXPECT_THAT(
261 Symbols,
262 UnorderedElementsAre(AllOf(
263 qName("Foo"),
264 includeHeader(URI::create(testPath(File.HeaderFilename)).toString()),
265 declURI(URI::create(testPath("private.h")).toString()))));
266}
267
268TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
269 TestTU TU;
270 TU.HeaderCode = "class Foo{};";
271 TU.HeaderFilename = "algorithm";
272
273 auto Symbols = runFuzzyFind(*TU.index(), "");
274 EXPECT_THAT(Symbols, ElementsAre(_));
275 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
276 "<algorithm>");
277}
278
279TEST(FileIndexTest, TemplateParamsInLabel) {
280 auto *Source = R"cpp(
281template <class Ty>
282class vector {
283};
284
285template <class Ty, class Arg>
286vector<Ty> make_vector(Arg A) {}
287)cpp";
288
289 FileIndex M(true);
290 update(M, "f", Source);
291
292 auto Symbols = runFuzzyFind(M, "");
293 EXPECT_THAT(Symbols,
294 UnorderedElementsAre(qName("vector"), qName("make_vector")));
295 auto It = Symbols.begin();
296 Symbol Vector = *It++;
297 Symbol MakeVector = *It++;
298 if (MakeVector.Name == "vector")
299 std::swap(MakeVector, Vector);
300
301 EXPECT_EQ(Vector.Signature, "<class Ty>");
302 EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>");
303
304 EXPECT_EQ(MakeVector.Signature, "<class Ty>(Arg A)");
305 EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})");
306}
307
308TEST(FileIndexTest, RebuildWithPreamble) {
309 auto FooCpp = testPath("foo.cpp");
310 auto FooH = testPath("foo.h");
311 // Preparse ParseInputs.
312 ParseInputs PI;
313 PI.CompileCommand.Directory = testRoot();
314 PI.CompileCommand.Filename = FooCpp;
315 PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp};
316
317 MockFS FS;
318 FS.Files[FooCpp] = "";
319 FS.Files[FooH] = R"cpp(
320 namespace ns_in_header {
321 int func_in_header();
322 }
323 )cpp";
324 PI.TFS = &FS;
325
326 PI.Contents = R"cpp(
327 #include "foo.h"
328 namespace ns_in_source {
329 int func_in_source();
330 }
331 )cpp";
332
333 // Rebuild the file.
334 IgnoreDiagnostics IgnoreDiags;
336
337 FileIndex Index(true);
338 bool IndexUpdated = false;
340 FooCpp, *CI, PI,
341 /*StoreInMemory=*/true,
342 [&](CapturedASTCtx ASTCtx,
343 std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
344 auto &Ctx = ASTCtx.getASTContext();
345 auto &PP = ASTCtx.getPreprocessor();
346 EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
347 IndexUpdated = true;
348 Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP, *PI);
349 });
350 ASSERT_TRUE(IndexUpdated);
351
352 // Check the index contains symbols from the preamble, but not from the main
353 // file.
354 FuzzyFindRequest Req;
355 Req.Query = "";
356 Req.Scopes = {"", "ns_in_header::"};
357
358 EXPECT_THAT(runFuzzyFind(Index, Req),
359 UnorderedElementsAre(qName("ns_in_header"),
360 qName("ns_in_header::func_in_header")));
361}
362
363TEST(FileIndexTest, Refs) {
364 const char *HeaderCode = "class Foo {};";
365 Annotations MainCode(R"cpp(
366 void f() {
367 $foo[[Foo]] foo;
368 }
369 )cpp");
370
371 auto Foo =
372 findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo");
373
374 RefsRequest Request;
375 Request.IDs = {Foo.ID};
376
377 FileIndex Index(true);
378 // Add test.cc
379 TestTU Test;
380 Test.HeaderCode = HeaderCode;
381 Test.Code = std::string(MainCode.code());
382 Test.Filename = "test.cc";
383 auto AST = Test.build();
384 Index.updateMain(testPath(Test.Filename), AST);
385 // Add test2.cc
386 TestTU Test2;
387 Test2.HeaderCode = HeaderCode;
388 Test2.Code = std::string(MainCode.code());
389 Test2.Filename = "test2.cc";
390 AST = Test2.build();
391 Index.updateMain(testPath(Test2.Filename), AST);
392
393 EXPECT_THAT(getRefs(Index, Foo.ID),
394 refsAre({AllOf(refRange(MainCode.range("foo")),
395 fileURI("unittest:///test.cc")),
396 AllOf(refRange(MainCode.range("foo")),
397 fileURI("unittest:///test2.cc"))}));
398}
399
400TEST(FileIndexTest, MacroRefs) {
401 Annotations HeaderCode(R"cpp(
402 #define $def1[[HEADER_MACRO]](X) (X+1)
403 )cpp");
404 Annotations MainCode(R"cpp(
405 #define $def2[[MAINFILE_MACRO]](X) (X+1)
406 void f() {
407 int a = $ref1[[HEADER_MACRO]](2);
408 int b = $ref2[[MAINFILE_MACRO]](1);
409 }
410 )cpp");
411
412 FileIndex Index(true);
413 // Add test.cc
414 TestTU Test;
415 Test.HeaderCode = std::string(HeaderCode.code());
416 Test.Code = std::string(MainCode.code());
417 Test.Filename = "test.cc";
418 auto AST = Test.build();
419 Index.updateMain(testPath(Test.Filename), AST);
420
421 auto HeaderMacro = findSymbol(Test.headerSymbols(), "HEADER_MACRO");
422 EXPECT_THAT(getRefs(Index, HeaderMacro.ID),
423 refsAre({AllOf(refRange(MainCode.range("ref1")),
424 fileURI("unittest:///test.cc"))}));
425
426 auto MainFileMacro = findSymbol(Test.headerSymbols(), "MAINFILE_MACRO");
427 EXPECT_THAT(getRefs(Index, MainFileMacro.ID),
428 refsAre({AllOf(refRange(MainCode.range("def2")),
429 fileURI("unittest:///test.cc")),
430 AllOf(refRange(MainCode.range("ref2")),
431 fileURI("unittest:///test.cc"))}));
432}
433
434TEST(FileIndexTest, CollectMacros) {
435 FileIndex M(true);
436 update(M, "f", "#define CLANGD 1");
437 EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
438}
439
440TEST(FileIndexTest, Relations) {
441 TestTU TU;
442 TU.Filename = "f.cpp";
443 TU.HeaderFilename = "f.h";
444 TU.HeaderCode = "class A {}; class B : public A {};";
445 auto AST = TU.build();
446 FileIndex Index(true);
447 Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
448 AST.getASTContext(), AST.getPreprocessor(),
449 AST.getPragmaIncludes());
450 SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
451 uint32_t Results = 0;
452 RelationsRequest Req;
453 Req.Subjects.insert(A);
454 Req.Predicate = RelationKind::BaseOf;
455 Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
456 EXPECT_EQ(Results, 1u);
457}
458
459TEST(FileIndexTest, RelationsMultiFile) {
460 TestWorkspace Workspace;
461 Workspace.addSource("Base.h", "class Base {};");
462 Workspace.addMainFile("A.cpp", R"cpp(
463 #include "Base.h"
464 class A : public Base {};
465 )cpp");
466 Workspace.addMainFile("B.cpp", R"cpp(
467 #include "Base.h"
468 class B : public Base {};
469 )cpp");
470
471 auto Index = Workspace.index();
472 FuzzyFindRequest FFReq;
473 FFReq.Query = "Base";
474 FFReq.AnyScope = true;
475 SymbolID Base;
476 Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; });
477
478 RelationsRequest Req;
479 Req.Subjects.insert(Base);
480 Req.Predicate = RelationKind::BaseOf;
481 uint32_t Results = 0;
482 Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
483 EXPECT_EQ(Results, 2u);
484}
485
486TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
487 TestTU TU;
488 TU.HeaderCode = "class Foo{};";
489 Annotations Main(R"cpp(
490 void f() {
491 [[Foo]] foo;
492 }
493 )cpp");
494 TU.Code = std::string(Main.code());
495 auto AST = TU.build();
496 FileIndex Index(true);
497 Index.updateMain(testPath(TU.Filename), AST);
498
499 // Expect to see references in main file, references in headers are excluded
500 // because we only index main AST.
501 EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID),
502 refsAre({refRange(Main.range())}));
503}
504
505TEST(FileIndexTest, MergeMainFileSymbols) {
506 const char *CommonHeader = "void foo();";
507 TestTU Header = TestTU::withCode(CommonHeader);
508 TestTU Cpp = TestTU::withCode("void foo() {}");
509 Cpp.Filename = "foo.cpp";
510 Cpp.HeaderFilename = "foo.h";
511 Cpp.HeaderCode = CommonHeader;
512
513 FileIndex Index(true);
514 auto HeaderAST = Header.build();
515 auto CppAST = Cpp.build();
516 Index.updateMain(testPath("foo.h"), HeaderAST);
517 Index.updateMain(testPath("foo.cpp"), CppAST);
518
519 auto Symbols = runFuzzyFind(Index, "");
520 // Check foo is merged, foo in Cpp wins (as we see the definition there).
521 EXPECT_THAT(Symbols, ElementsAre(AllOf(declURI("unittest:///foo.h"),
522 defURI("unittest:///foo.cpp"),
523 hasOrign(SymbolOrigin::Merge))));
524}
525
526TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
527 FileSymbols FS(IndexContents::All, true);
528 FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
529 FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
530 EXPECT_THAT(
532 ""),
533 UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)),
534 AllOf(qName("2"), numReferences(0u)),
535 AllOf(qName("3"), numReferences(0u))));
536}
537
538TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
539 FileSymbols FS(IndexContents::All, true);
540 FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
541 true);
542 FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
543 false);
544 FS.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr,
545 true);
546 FS.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr,
547 false);
548 FS.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr,
549 true);
550 FS.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr,
551 false);
552 EXPECT_THAT(
554 ""),
555 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
556 AllOf(qName("2"), numReferences(1u)),
557 AllOf(qName("3"), numReferences(1u))));
558}
559
560TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
561 FileIndex M(true);
562 TestTU File;
563 File.HeaderFilename = "a.h";
564
565 File.Filename = "f1.cpp";
566 File.HeaderCode = "int a;";
567 auto AST = File.build();
568 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
569 AST.getASTContext(), AST.getPreprocessor(),
570 AST.getPragmaIncludes());
571 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a")));
572
573 File.Filename = "f2.cpp";
574 File.HeaderCode = "int b;";
575 AST = File.build();
576 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
577 AST.getASTContext(), AST.getPreprocessor(),
578 AST.getPragmaIncludes());
579 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b")));
580}
581
582// Verifies that concurrent calls to updateMain don't "lose" any updates.
583TEST(FileIndexTest, Threadsafety) {
584 FileIndex M(true);
585 Notification Go;
586
587 constexpr int Count = 10;
588 {
589 // Set up workers to concurrently call updateMain() with separate files.
590 AsyncTaskRunner Pool;
591 for (unsigned I = 0; I < Count; ++I) {
592 auto TU = TestTU::withCode(llvm::formatv("int xxx{0};", I).str());
593 TU.Filename = llvm::formatv("x{0}.c", I).str();
594 Pool.runAsync(TU.Filename, [&, Filename(testPath(TU.Filename)),
595 AST(TU.build())]() mutable {
596 Go.wait();
597 M.updateMain(Filename, AST);
598 });
599 }
600 // On your marks, get set...
601 Go.notify();
602 }
603
604 EXPECT_THAT(runFuzzyFind(M, "xxx"), ::testing::SizeIs(Count));
605}
606
607TEST(FileShardedIndexTest, Sharding) {
608 auto AHeaderUri = URI::create(testPath("a.h")).toString();
609 auto BHeaderUri = URI::create(testPath("b.h")).toString();
610 auto BSourceUri = URI::create(testPath("b.cc")).toString();
611
612 auto Sym1 = symbol("1");
613 Sym1.CanonicalDeclaration.FileURI = AHeaderUri.c_str();
614
615 auto Sym2 = symbol("2");
616 Sym2.CanonicalDeclaration.FileURI = BHeaderUri.c_str();
617 Sym2.Definition.FileURI = BSourceUri.c_str();
618
619 auto Sym3 = symbol("3"); // not stored
620
621 IndexFileIn IF;
622 {
623 SymbolSlab::Builder B;
624 // Should be stored in only a.h
625 B.insert(Sym1);
626 // Should be stored in both b.h and b.cc
627 B.insert(Sym2);
628 IF.Symbols.emplace(std::move(B).build());
629 }
630 {
631 // Should be stored in b.cc
632 IF.Refs.emplace(std::move(*refSlab(Sym1.ID, BSourceUri.c_str())));
633 }
634 {
635 RelationSlab::Builder B;
636 // Should be stored in a.h and b.h
637 B.insert(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID});
638 // Should be stored in a.h and b.h
639 B.insert(Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID});
640 // Should be stored in a.h (where Sym1 is stored) even though
641 // the relation is dangling as Sym3 is unknown.
642 B.insert(Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID});
643 IF.Relations.emplace(std::move(B).build());
644 }
645
646 IF.Sources.emplace();
647 IncludeGraph &IG = *IF.Sources;
648 {
649 // b.cc includes b.h
650 auto &Node = IG[BSourceUri];
651 Node.DirectIncludes = {BHeaderUri};
652 Node.URI = BSourceUri;
653 }
654 {
655 // b.h includes a.h
656 auto &Node = IG[BHeaderUri];
657 Node.DirectIncludes = {AHeaderUri};
658 Node.URI = BHeaderUri;
659 }
660 {
661 // a.h includes nothing.
662 auto &Node = IG[AHeaderUri];
663 Node.DirectIncludes = {};
664 Node.URI = AHeaderUri;
665 }
666
667 IF.Cmd = tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out");
668
669 FileShardedIndex ShardedIndex(std::move(IF));
670 ASSERT_THAT(ShardedIndex.getAllSources(),
671 UnorderedElementsAre(AHeaderUri, BHeaderUri, BSourceUri));
672
673 {
674 auto Shard = ShardedIndex.getShard(AHeaderUri);
675 ASSERT_TRUE(Shard);
676 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("1")));
677 EXPECT_THAT(*Shard->Refs, IsEmpty());
678 EXPECT_THAT(
679 *Shard->Relations,
680 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
681 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID},
682 Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID}));
683 ASSERT_THAT(Shard->Sources->keys(), UnorderedElementsAre(AHeaderUri));
684 EXPECT_THAT(Shard->Sources->lookup(AHeaderUri).DirectIncludes, IsEmpty());
685 EXPECT_TRUE(Shard->Cmd);
686 }
687 {
688 auto Shard = ShardedIndex.getShard(BHeaderUri);
689 ASSERT_TRUE(Shard);
690 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
691 EXPECT_THAT(*Shard->Refs, IsEmpty());
692 EXPECT_THAT(
693 *Shard->Relations,
694 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
695 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID}));
696 ASSERT_THAT(Shard->Sources->keys(),
697 UnorderedElementsAre(BHeaderUri, AHeaderUri));
698 EXPECT_THAT(Shard->Sources->lookup(BHeaderUri).DirectIncludes,
699 UnorderedElementsAre(AHeaderUri));
700 EXPECT_TRUE(Shard->Cmd);
701 }
702 {
703 auto Shard = ShardedIndex.getShard(BSourceUri);
704 ASSERT_TRUE(Shard);
705 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
706 EXPECT_THAT(*Shard->Refs, UnorderedElementsAre(Pair(Sym1.ID, _)));
707 EXPECT_THAT(*Shard->Relations, IsEmpty());
708 ASSERT_THAT(Shard->Sources->keys(),
709 UnorderedElementsAre(BSourceUri, BHeaderUri));
710 EXPECT_THAT(Shard->Sources->lookup(BSourceUri).DirectIncludes,
711 UnorderedElementsAre(BHeaderUri));
712 EXPECT_TRUE(Shard->Cmd);
713 }
714}
715
716TEST(FileIndexTest, Profile) {
717 FileIndex FI(true);
718
719 auto FileName = testPath("foo.cpp");
720 auto AST = TestTU::withHeaderCode("int a;").build();
721 FI.updateMain(FileName, AST);
722 FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(),
723 AST.getPragmaIncludes());
724
725 llvm::BumpPtrAllocator Alloc;
726 MemoryTree MT(&Alloc);
727 FI.profile(MT);
728 ASSERT_THAT(MT.children(),
729 UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _)));
730
731 ASSERT_THAT(MT.child("preamble").children(),
732 UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
733 ASSERT_THAT(MT.child("main_file").children(),
734 UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
735
736 ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U));
737 ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U));
738}
739
740TEST(FileSymbolsTest, Profile) {
741 FileSymbols FS(IndexContents::All, true);
742 FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
743 FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
744 FS.update("f3", nullptr, nullptr,
745 relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}),
746 false);
747 llvm::BumpPtrAllocator Alloc;
748 MemoryTree MT(&Alloc);
749 FS.profile(MT);
750 ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _),
751 Pair("f3", _)));
752 EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _)));
753 EXPECT_THAT(MT.child("f1").total(), Gt(0U));
754 EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _)));
755 EXPECT_THAT(MT.child("f2").total(), Gt(0U));
756 EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _)));
757 EXPECT_THAT(MT.child("f3").total(), Gt(0U));
758}
759
760TEST(FileIndexTest, MacrosFromMainFile) {
761 FileIndex Idx(true);
762 TestTU TU;
763 TU.Code = "#pragma once\n#define FOO";
764 TU.Filename = "foo.h";
765 auto AST = TU.build();
766 Idx.updateMain(testPath(TU.Filename), AST);
767
768 auto Slab = runFuzzyFind(Idx, "");
769 auto &FooSymbol = findSymbol(Slab, "FOO");
770 EXPECT_TRUE(FooSymbol.Flags & Symbol::IndexedForCodeCompletion);
771}
772
773} // namespace
774} // namespace clangd
775} // namespace clang
Bracket::Index Pair
Definition: Bracket.cpp:80
std::vector< CodeCompletionResult > Results
MATCHER_P(refRange, Range, "")
IgnoringDiagConsumer IgnoreDiags
CharSourceRange Range
SourceRange for the file name.
std::string Filename
Filename as a string.
StringRef FileName
::clang::DynTypedNode Node
const google::protobuf::Message & M
Definition: Server.cpp:356
std::unique_ptr< CompilerInvocation > CI
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:208
::testing::Matcher< const RefSlab & > refsAre(std::vector<::testing::Matcher< Ref > > Matchers)
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:95
static const char * toString(OffsetEncoding OE)
Definition: Protocol.cpp:1595
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
Definition: Preamble.cpp:591
TEST(BackgroundQueueTest, Priority)
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
Definition: SyncAPI.cpp:138
Symbol symbol(llvm::StringRef QName)
Definition: TestIndex.cpp:17
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:101
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:185
@ Type
An inlay hint that for a type annotation.
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
Definition: SyncAPI.cpp:125
const char * testRoot()
Definition: TestFS.cpp:85
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: sample.h:4
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:141
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:41
ParsedAST build() const
Definition: TestTU.cpp:114
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:42
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36