[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clangd / unittests / FileIndexTests.cpp
blob9f713564b2c01f07baea5c7b724a645907f7595c
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 //===----------------------------------------------------------------------===//
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>
37 using ::testing::_;
38 using ::testing::AllOf;
39 using ::testing::Contains;
40 using ::testing::ElementsAre;
41 using ::testing::Gt;
42 using ::testing::IsEmpty;
43 using ::testing::Pair;
44 using ::testing::UnorderedElementsAre;
46 MATCHER_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);
52 MATCHER_P(fileURI, F, "") { return llvm::StringRef(arg.Location.FileURI) == F; }
53 MATCHER_P(declURI, U, "") {
54 return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U;
56 MATCHER_P(defURI, U, "") {
57 return llvm::StringRef(arg.Definition.FileURI) == U;
59 MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; }
60 MATCHER_P(numReferences, N, "") { return arg.References == N; }
61 MATCHER_P(hasOrign, O, "") { return bool(arg.Origin & O); }
63 MATCHER_P(includeHeader, P, "") {
64 return (arg.IncludeHeaders.size() == 1) &&
65 (arg.IncludeHeaders.begin()->IncludeHeader == P);
68 namespace clang {
69 namespace clangd {
70 namespace {
71 ::testing::Matcher<const RefSlab &>
72 refsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
73 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
76 Symbol symbol(llvm::StringRef ID) {
77 Symbol Sym;
78 Sym.ID = SymbolID(ID);
79 Sym.Name = ID;
80 return Sym;
83 std::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());
90 std::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());
99 std::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());
106 TEST(FileSymbolsTest, UpdateAndGet) {
107 FileSymbols FS(IndexContents::All);
108 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
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")}));
118 TEST(FileSymbolsTest, Overlap) {
119 FileSymbols FS(IndexContents::All);
120 FS.update("f1", numSlab(1, 3), nullptr, nullptr, false);
121 FS.update("f2", numSlab(3, 5), nullptr, nullptr, false);
122 for (auto Type : {IndexType::Light, IndexType::Heavy})
123 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""),
124 UnorderedElementsAre(qName("1"), qName("2"), qName("3"),
125 qName("4"), qName("5")));
128 TEST(FileSymbolsTest, MergeOverlap) {
129 FileSymbols FS(IndexContents::All);
130 auto OneSymboSlab = [](Symbol Sym) {
131 SymbolSlab::Builder S;
132 S.insert(Sym);
133 return std::make_unique<SymbolSlab>(std::move(S).build());
135 auto X1 = symbol("x");
136 X1.CanonicalDeclaration.FileURI = "file:///x1";
137 auto X2 = symbol("x");
138 X2.Definition.FileURI = "file:///x2";
140 FS.update("f1", OneSymboSlab(X1), nullptr, nullptr, false);
141 FS.update("f2", OneSymboSlab(X2), nullptr, nullptr, false);
142 for (auto Type : {IndexType::Light, IndexType::Heavy})
143 EXPECT_THAT(
144 runFuzzyFind(*FS.buildIndex(Type, DuplicateHandling::Merge), "x"),
145 UnorderedElementsAre(
146 AllOf(qName("x"), declURI("file:///x1"), defURI("file:///x2"))));
149 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
150 FileSymbols FS(IndexContents::All);
152 SymbolID ID("1");
153 FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
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")}));
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());
165 EXPECT_THAT(runFuzzyFind(*Symbols, ""),
166 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
167 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
170 // Adds Basename.cpp, which includes Basename.h, which contains Code.
171 void 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());
182 TEST(FileIndexTest, CustomizedURIScheme) {
183 FileIndex M;
184 update(M, "f", "class string {};");
186 EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
189 TEST(FileIndexTest, IndexAST) {
190 FileIndex M;
191 update(M, "f1", "namespace ns { void f() {} class X {}; }");
193 FuzzyFindRequest Req;
194 Req.Query = "";
195 Req.Scopes = {"ns::"};
196 EXPECT_THAT(runFuzzyFind(M, Req),
197 UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
200 TEST(FileIndexTest, NoLocal) {
201 FileIndex M;
202 update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
204 EXPECT_THAT(
205 runFuzzyFind(M, ""),
206 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
209 TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
210 FileIndex M;
211 update(M, "f1", "namespace ns { void f() {} class X {}; }");
212 update(M, "f2", "namespace ns { void ff() {} class X {}; }");
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")));
221 TEST(FileIndexTest, ClassMembers) {
222 FileIndex M;
223 update(M, "f1", "class X { static int m1; int m2; static void f(); };");
225 EXPECT_THAT(runFuzzyFind(M, ""),
226 UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"),
227 qName("X::f")));
230 TEST(FileIndexTest, IncludeCollected) {
231 FileIndex M;
232 update(
233 M, "f",
234 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
236 auto Symbols = runFuzzyFind(M, "");
237 EXPECT_THAT(Symbols, ElementsAre(_));
238 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
239 "<the/good/header.h>");
242 TEST(FileIndexTest, IWYUPragmaExport) {
243 FileIndex M;
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());
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()))));
268 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
269 TestTU TU;
270 TU.HeaderCode = "class Foo{};";
271 TU.HeaderFilename = "algorithm";
273 auto Symbols = runFuzzyFind(*TU.index(), "");
274 EXPECT_THAT(Symbols, ElementsAre(_));
275 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
276 "<algorithm>");
279 TEST(FileIndexTest, TemplateParamsInLabel) {
280 auto *Source = R"cpp(
281 template <class Ty>
282 class vector {
285 template <class Ty, class Arg>
286 vector<Ty> make_vector(Arg A) {}
287 )cpp";
289 FileIndex M;
290 update(M, "f", Source);
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);
301 EXPECT_EQ(Vector.Signature, "<class Ty>");
302 EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>");
304 EXPECT_EQ(MakeVector.Signature, "<class Ty>(Arg A)");
305 EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})");
308 TEST(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};
317 MockFS FS;
318 FS.Files[FooCpp] = "";
319 FS.Files[FooH] = R"cpp(
320 namespace ns_in_header {
321 int func_in_header();
323 )cpp";
324 PI.TFS = &FS;
326 PI.Contents = R"cpp(
327 #include "foo.h"
328 namespace ns_in_source {
329 int func_in_source();
331 )cpp";
333 // Rebuild the file.
334 IgnoreDiagnostics IgnoreDiags;
335 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
337 FileIndex Index;
338 bool IndexUpdated = false;
339 buildPreamble(
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);
350 ASSERT_TRUE(IndexUpdated);
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::"};
358 EXPECT_THAT(runFuzzyFind(Index, Req),
359 UnorderedElementsAre(qName("ns_in_header"),
360 qName("ns_in_header::func_in_header")));
363 TEST(FileIndexTest, Refs) {
364 const char *HeaderCode = "class Foo {};";
365 Annotations MainCode(R"cpp(
366 void f() {
367 $foo[[Foo]] foo;
369 )cpp");
371 auto Foo =
372 findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo");
374 RefsRequest Request;
375 Request.IDs = {Foo.ID};
377 FileIndex Index;
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);
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"))}));
400 TEST(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);
410 )cpp");
412 FileIndex Index;
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);
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"))}));
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"))}));
434 TEST(FileIndexTest, CollectMacros) {
435 FileIndex M;
436 update(M, "f", "#define CLANGD 1");
437 EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
440 TEST(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;
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);
459 TEST(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");
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; });
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);
486 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
487 TestTU TU;
488 TU.HeaderCode = "class Foo{};";
489 Annotations Main(R"cpp(
490 void f() {
491 [[Foo]] foo;
493 )cpp");
494 TU.Code = std::string(Main.code());
495 auto AST = TU.build();
496 FileIndex Index;
497 Index.updateMain(testPath(TU.Filename), AST);
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())}));
505 TEST(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;
513 FileIndex Index;
514 auto HeaderAST = Header.build();
515 auto CppAST = Cpp.build();
516 Index.updateMain(testPath("foo.h"), HeaderAST);
517 Index.updateMain(testPath("foo.cpp"), CppAST);
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))));
526 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
527 FileSymbols FS(IndexContents::All);
528 FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
529 FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
530 EXPECT_THAT(
531 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge),
532 ""),
533 UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)),
534 AllOf(qName("2"), numReferences(0u)),
535 AllOf(qName("3"), numReferences(0u))));
538 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
539 FileSymbols FS(IndexContents::All);
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(
553 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge),
554 ""),
555 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
556 AllOf(qName("2"), numReferences(1u)),
557 AllOf(qName("3"), numReferences(1u))));
560 TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
561 FileIndex M;
562 TestTU File;
563 File.HeaderFilename = "a.h";
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")));
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")));
582 // Verifies that concurrent calls to updateMain don't "lose" any updates.
583 TEST(FileIndexTest, Threadsafety) {
584 FileIndex M;
585 Notification Go;
587 constexpr int Count = 10;
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);
600 // On your marks, get set...
601 Go.notify();
604 EXPECT_THAT(runFuzzyFind(M, "xxx"), ::testing::SizeIs(Count));
607 TEST(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();
612 auto Sym1 = symbol("1");
613 Sym1.CanonicalDeclaration.FileURI = AHeaderUri.c_str();
615 auto Sym2 = symbol("2");
616 Sym2.CanonicalDeclaration.FileURI = BHeaderUri.c_str();
617 Sym2.Definition.FileURI = BSourceUri.c_str();
619 auto Sym3 = symbol("3"); // not stored
621 IndexFileIn IF;
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());
631 // Should be stored in b.cc
632 IF.Refs.emplace(std::move(*refSlab(Sym1.ID, BSourceUri.c_str())));
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());
646 IF.Sources.emplace();
647 IncludeGraph &IG = *IF.Sources;
649 // b.cc includes b.h
650 auto &Node = IG[BSourceUri];
651 Node.DirectIncludes = {BHeaderUri};
652 Node.URI = BSourceUri;
655 // b.h includes a.h
656 auto &Node = IG[BHeaderUri];
657 Node.DirectIncludes = {AHeaderUri};
658 Node.URI = BHeaderUri;
661 // a.h includes nothing.
662 auto &Node = IG[AHeaderUri];
663 Node.DirectIncludes = {};
664 Node.URI = AHeaderUri;
667 IF.Cmd = tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out");
669 FileShardedIndex ShardedIndex(std::move(IF));
670 ASSERT_THAT(ShardedIndex.getAllSources(),
671 UnorderedElementsAre(AHeaderUri, BHeaderUri, BSourceUri));
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);
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);
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);
716 TEST(FileIndexTest, Profile) {
717 FileIndex FI;
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());
725 llvm::BumpPtrAllocator Alloc;
726 MemoryTree MT(&Alloc);
727 FI.profile(MT);
728 ASSERT_THAT(MT.children(),
729 UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _)));
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", _)));
736 ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U));
737 ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U));
740 TEST(FileSymbolsTest, Profile) {
741 FileSymbols FS(IndexContents::All);
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));
760 TEST(FileIndexTest, MacrosFromMainFile) {
761 FileIndex Idx;
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);
768 auto Slab = runFuzzyFind(Idx, "");
769 auto &FooSymbol = findSymbol(Slab, "FOO");
770 EXPECT_TRUE(FooSymbol.Flags & Symbol::IndexedForCodeCompletion);
773 } // namespace
774 } // namespace clangd
775 } // namespace clang