1 //===-- FileIndexTests.cpp ---------------------------*- C++ -*-----------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
12 #include "ParsedAST.h"
16 #include "TestWorkspace.h"
18 #include "index/CanonicalIncludes.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/Lex/Preprocessor.h"
29 #include "clang/Tooling/CompilationDatabase.h"
30 #include "llvm/ADT/ArrayRef.h"
31 #include "llvm/Support/Allocator.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
38 using ::testing::AllOf
;
39 using ::testing::Contains
;
40 using ::testing::ElementsAre
;
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
); }
66 ::testing::Matcher
<const RefSlab
&>
67 refsAre(std::vector
<::testing::Matcher
<Ref
>> Matchers
) {
68 return ElementsAre(::testing::Pair(_
, UnorderedElementsAreArray(Matchers
)));
71 Symbol
symbol(llvm::StringRef ID
) {
73 Sym
.ID
= SymbolID(ID
);
78 std::unique_ptr
<SymbolSlab
> numSlab(int Begin
, int End
) {
79 SymbolSlab::Builder Slab
;
80 for (int I
= Begin
; I
<= End
; I
++)
81 Slab
.insert(symbol(std::to_string(I
)));
82 return std::make_unique
<SymbolSlab
>(std::move(Slab
).build());
85 std::unique_ptr
<RefSlab
> refSlab(const SymbolID
&ID
, const char *Path
) {
86 RefSlab::Builder Slab
;
88 R
.Location
.FileURI
= Path
;
89 R
.Kind
= RefKind::Reference
;
91 return std::make_unique
<RefSlab
>(std::move(Slab
).build());
94 std::unique_ptr
<RelationSlab
> relSlab(llvm::ArrayRef
<const Relation
> Rels
) {
95 RelationSlab::Builder RelBuilder
;
96 for (auto &Rel
: Rels
)
97 RelBuilder
.insert(Rel
);
98 return std::make_unique
<RelationSlab
>(std::move(RelBuilder
).build());
101 TEST(FileSymbolsTest
, UpdateAndGet
) {
102 FileSymbols
FS(IndexContents::All
);
103 EXPECT_THAT(runFuzzyFind(*FS
.buildIndex(IndexType::Light
), ""), IsEmpty());
105 FS
.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr,
107 EXPECT_THAT(runFuzzyFind(*FS
.buildIndex(IndexType::Light
), ""),
108 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
109 EXPECT_THAT(getRefs(*FS
.buildIndex(IndexType::Light
), SymbolID("1")),
110 refsAre({fileURI("f1.cc")}));
113 TEST(FileSymbolsTest
, Overlap
) {
114 FileSymbols
FS(IndexContents::All
);
115 FS
.update("f1", numSlab(1, 3), nullptr, nullptr, false);
116 FS
.update("f2", numSlab(3, 5), nullptr, nullptr, false);
117 for (auto Type
: {IndexType::Light
, IndexType::Heavy
})
118 EXPECT_THAT(runFuzzyFind(*FS
.buildIndex(Type
), ""),
119 UnorderedElementsAre(qName("1"), qName("2"), qName("3"),
120 qName("4"), qName("5")));
123 TEST(FileSymbolsTest
, MergeOverlap
) {
124 FileSymbols
FS(IndexContents::All
);
125 auto OneSymboSlab
= [](Symbol Sym
) {
126 SymbolSlab::Builder S
;
128 return std::make_unique
<SymbolSlab
>(std::move(S
).build());
130 auto X1
= symbol("x");
131 X1
.CanonicalDeclaration
.FileURI
= "file:///x1";
132 auto X2
= symbol("x");
133 X2
.Definition
.FileURI
= "file:///x2";
135 FS
.update("f1", OneSymboSlab(X1
), nullptr, nullptr, false);
136 FS
.update("f2", OneSymboSlab(X2
), nullptr, nullptr, false);
137 for (auto Type
: {IndexType::Light
, IndexType::Heavy
})
139 runFuzzyFind(*FS
.buildIndex(Type
, DuplicateHandling::Merge
), "x"),
140 UnorderedElementsAre(
141 AllOf(qName("x"), declURI("file:///x1"), defURI("file:///x2"))));
144 TEST(FileSymbolsTest
, SnapshotAliveAfterRemove
) {
145 FileSymbols
FS(IndexContents::All
);
148 FS
.update("f1", numSlab(1, 3), refSlab(ID
, "f1.cc"), nullptr, false);
150 auto Symbols
= FS
.buildIndex(IndexType::Light
);
151 EXPECT_THAT(runFuzzyFind(*Symbols
, ""),
152 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
153 EXPECT_THAT(getRefs(*Symbols
, ID
), refsAre({fileURI("f1.cc")}));
155 FS
.update("f1", nullptr, nullptr, nullptr, false);
156 auto Empty
= FS
.buildIndex(IndexType::Light
);
157 EXPECT_THAT(runFuzzyFind(*Empty
, ""), IsEmpty());
158 EXPECT_THAT(getRefs(*Empty
, ID
), ElementsAre());
160 EXPECT_THAT(runFuzzyFind(*Symbols
, ""),
161 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
162 EXPECT_THAT(getRefs(*Symbols
, ID
), refsAre({fileURI("f1.cc")}));
165 // Adds Basename.cpp, which includes Basename.h, which contains Code.
166 void update(FileIndex
&M
, llvm::StringRef Basename
, llvm::StringRef Code
) {
168 File
.Filename
= (Basename
+ ".cpp").str();
169 File
.HeaderFilename
= (Basename
+ ".h").str();
170 File
.HeaderCode
= std::string(Code
);
171 auto AST
= File
.build();
172 M
.updatePreamble(testPath(File
.Filename
), /*Version=*/"null",
173 AST
.getASTContext(), AST
.getPreprocessor(),
174 AST
.getCanonicalIncludes());
177 TEST(FileIndexTest
, CustomizedURIScheme
) {
179 update(M
, "f", "class string {};");
181 EXPECT_THAT(runFuzzyFind(M
, ""), ElementsAre(declURI("unittest:///f.h")));
184 TEST(FileIndexTest
, IndexAST
) {
186 update(M
, "f1", "namespace ns { void f() {} class X {}; }");
188 FuzzyFindRequest Req
;
190 Req
.Scopes
= {"ns::"};
191 EXPECT_THAT(runFuzzyFind(M
, Req
),
192 UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
195 TEST(FileIndexTest
, NoLocal
) {
197 update(M
, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
201 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
204 TEST(FileIndexTest
, IndexMultiASTAndDeduplicate
) {
206 update(M
, "f1", "namespace ns { void f() {} class X {}; }");
207 update(M
, "f2", "namespace ns { void ff() {} class X {}; }");
209 FuzzyFindRequest Req
;
210 Req
.Scopes
= {"ns::"};
212 runFuzzyFind(M
, Req
),
213 UnorderedElementsAre(qName("ns::f"), qName("ns::X"), qName("ns::ff")));
216 TEST(FileIndexTest
, ClassMembers
) {
218 update(M
, "f1", "class X { static int m1; int m2; static void f(); };");
220 EXPECT_THAT(runFuzzyFind(M
, ""),
221 UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"),
225 TEST(FileIndexTest
, IncludeCollected
) {
229 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
231 auto Symbols
= runFuzzyFind(M
, "");
232 EXPECT_THAT(Symbols
, ElementsAre(_
));
233 EXPECT_THAT(Symbols
.begin()->IncludeHeaders
.front().IncludeHeader
,
234 "<the/good/header.h>");
237 TEST(FileIndexTest
, HasSystemHeaderMappingsInPreamble
) {
239 TU
.HeaderCode
= "class Foo{};";
240 TU
.HeaderFilename
= "algorithm";
242 auto Symbols
= runFuzzyFind(*TU
.index(), "");
243 EXPECT_THAT(Symbols
, ElementsAre(_
));
244 EXPECT_THAT(Symbols
.begin()->IncludeHeaders
.front().IncludeHeader
,
248 TEST(FileIndexTest
, TemplateParamsInLabel
) {
249 auto *Source
= R
"cpp(
254 template <class Ty, class Arg>
255 vector<Ty> make_vector(Arg A) {}
259 update(M
, "f", Source
);
261 auto Symbols
= runFuzzyFind(M
, "");
263 UnorderedElementsAre(qName("vector"), qName("make_vector")));
264 auto It
= Symbols
.begin();
265 Symbol Vector
= *It
++;
266 Symbol MakeVector
= *It
++;
267 if (MakeVector
.Name
== "vector")
268 std::swap(MakeVector
, Vector
);
270 EXPECT_EQ(Vector
.Signature
, "<class Ty>");
271 EXPECT_EQ(Vector
.CompletionSnippetSuffix
, "<${1:class Ty}>");
273 EXPECT_EQ(MakeVector
.Signature
, "<class Ty>(Arg A)");
274 EXPECT_EQ(MakeVector
.CompletionSnippetSuffix
, "<${1:class Ty}>(${2:Arg A})");
277 TEST(FileIndexTest
, RebuildWithPreamble
) {
278 auto FooCpp
= testPath("foo.cpp");
279 auto FooH
= testPath("foo.h");
280 // Preparse ParseInputs.
282 PI
.CompileCommand
.Directory
= testRoot();
283 PI
.CompileCommand
.Filename
= FooCpp
;
284 PI
.CompileCommand
.CommandLine
= {"clang", "-xc++", FooCpp
};
287 FS
.Files
[FooCpp
] = "";
288 FS
.Files
[FooH
] = R
"cpp(
289 namespace ns_in_header {
290 int func_in_header();
297 namespace ns_in_source {
298 int func_in_source();
303 IgnoreDiagnostics IgnoreDiags
;
304 auto CI
= buildCompilerInvocation(PI
, IgnoreDiags
);
307 bool IndexUpdated
= false;
310 /*StoreInMemory=*/true,
311 [&](CapturedASTCtx ASTCtx
,
312 const std::shared_ptr
<const CanonicalIncludes
> CanonIncludes
) {
313 auto &Ctx
= ASTCtx
.getASTContext();
314 auto &PP
= ASTCtx
.getPreprocessor();
315 EXPECT_FALSE(IndexUpdated
) << "Expected only a single index update";
317 Index
.updatePreamble(FooCpp
, /*Version=*/"null", Ctx
, PP
,
320 ASSERT_TRUE(IndexUpdated
);
322 // Check the index contains symbols from the preamble, but not from the main
324 FuzzyFindRequest Req
;
326 Req
.Scopes
= {"", "ns_in_header::"};
328 EXPECT_THAT(runFuzzyFind(Index
, Req
),
329 UnorderedElementsAre(qName("ns_in_header"),
330 qName("ns_in_header::func_in_header")));
333 TEST(FileIndexTest
, Refs
) {
334 const char *HeaderCode
= "class Foo {};";
335 Annotations
MainCode(R
"cpp(
342 findSymbol(TestTU::withHeaderCode(HeaderCode
).headerSymbols(), "Foo");
345 Request
.IDs
= {Foo
.ID
};
350 Test
.HeaderCode
= HeaderCode
;
351 Test
.Code
= std::string(MainCode
.code());
352 Test
.Filename
= "test.cc";
353 auto AST
= Test
.build();
354 Index
.updateMain(testPath(Test
.Filename
), AST
);
357 Test2
.HeaderCode
= HeaderCode
;
358 Test2
.Code
= std::string(MainCode
.code());
359 Test2
.Filename
= "test2.cc";
361 Index
.updateMain(testPath(Test2
.Filename
), AST
);
363 EXPECT_THAT(getRefs(Index
, Foo
.ID
),
364 refsAre({AllOf(refRange(MainCode
.range("foo")),
365 fileURI("unittest:///test.cc")),
366 AllOf(refRange(MainCode
.range("foo")),
367 fileURI("unittest:///test2.cc"))}));
370 TEST(FileIndexTest
, MacroRefs
) {
371 Annotations
HeaderCode(R
"cpp(
372 #define $def1[[HEADER_MACRO]](X) (X+1)
374 Annotations
MainCode(R
"cpp(
375 #define $def2[[MAINFILE_MACRO]](X) (X+1)
377 int a = $ref1[[HEADER_MACRO]](2);
378 int b = $ref2[[MAINFILE_MACRO]](1);
385 Test
.HeaderCode
= std::string(HeaderCode
.code());
386 Test
.Code
= std::string(MainCode
.code());
387 Test
.Filename
= "test.cc";
388 auto AST
= Test
.build();
389 Index
.updateMain(testPath(Test
.Filename
), AST
);
391 auto HeaderMacro
= findSymbol(Test
.headerSymbols(), "HEADER_MACRO");
392 EXPECT_THAT(getRefs(Index
, HeaderMacro
.ID
),
393 refsAre({AllOf(refRange(MainCode
.range("ref1")),
394 fileURI("unittest:///test.cc"))}));
396 auto MainFileMacro
= findSymbol(Test
.headerSymbols(), "MAINFILE_MACRO");
397 EXPECT_THAT(getRefs(Index
, MainFileMacro
.ID
),
398 refsAre({AllOf(refRange(MainCode
.range("def2")),
399 fileURI("unittest:///test.cc")),
400 AllOf(refRange(MainCode
.range("ref2")),
401 fileURI("unittest:///test.cc"))}));
404 TEST(FileIndexTest
, CollectMacros
) {
406 update(M
, "f", "#define CLANGD 1");
407 EXPECT_THAT(runFuzzyFind(M
, ""), Contains(qName("CLANGD")));
410 TEST(FileIndexTest
, Relations
) {
412 TU
.Filename
= "f.cpp";
413 TU
.HeaderFilename
= "f.h";
414 TU
.HeaderCode
= "class A {}; class B : public A {};";
415 auto AST
= TU
.build();
417 Index
.updatePreamble(testPath(TU
.Filename
), /*Version=*/"null",
418 AST
.getASTContext(), AST
.getPreprocessor(),
419 AST
.getCanonicalIncludes());
420 SymbolID A
= findSymbol(TU
.headerSymbols(), "A").ID
;
421 uint32_t Results
= 0;
422 RelationsRequest Req
;
423 Req
.Subjects
.insert(A
);
424 Req
.Predicate
= RelationKind::BaseOf
;
425 Index
.relations(Req
, [&](const SymbolID
&, const Symbol
&) { ++Results
; });
426 EXPECT_EQ(Results
, 1u);
429 TEST(FileIndexTest
, RelationsMultiFile
) {
430 TestWorkspace Workspace
;
431 Workspace
.addSource("Base.h", "class Base {};");
432 Workspace
.addMainFile("A.cpp", R
"cpp(
434 class A : public Base {};
436 Workspace
.addMainFile("B.cpp", R
"cpp(
438 class B : public Base {};
441 auto Index
= Workspace
.index();
442 FuzzyFindRequest FFReq
;
443 FFReq
.Query
= "Base";
444 FFReq
.AnyScope
= true;
446 Index
->fuzzyFind(FFReq
, [&](const Symbol
&S
) { Base
= S
.ID
; });
448 RelationsRequest Req
;
449 Req
.Subjects
.insert(Base
);
450 Req
.Predicate
= RelationKind::BaseOf
;
451 uint32_t Results
= 0;
452 Index
->relations(Req
, [&](const SymbolID
&, const Symbol
&) { ++Results
; });
453 EXPECT_EQ(Results
, 2u);
456 TEST(FileIndexTest
, ReferencesInMainFileWithPreamble
) {
458 TU
.HeaderCode
= "class Foo{};";
459 Annotations
Main(R
"cpp(
464 TU
.Code
= std::string(Main
.code());
465 auto AST
= TU
.build();
467 Index
.updateMain(testPath(TU
.Filename
), AST
);
469 // Expect to see references in main file, references in headers are excluded
470 // because we only index main AST.
471 EXPECT_THAT(getRefs(Index
, findSymbol(TU
.headerSymbols(), "Foo").ID
),
472 refsAre({refRange(Main
.range())}));
475 TEST(FileIndexTest
, MergeMainFileSymbols
) {
476 const char *CommonHeader
= "void foo();";
477 TestTU Header
= TestTU::withCode(CommonHeader
);
478 TestTU Cpp
= TestTU::withCode("void foo() {}");
479 Cpp
.Filename
= "foo.cpp";
480 Cpp
.HeaderFilename
= "foo.h";
481 Cpp
.HeaderCode
= CommonHeader
;
484 auto HeaderAST
= Header
.build();
485 auto CppAST
= Cpp
.build();
486 Index
.updateMain(testPath("foo.h"), HeaderAST
);
487 Index
.updateMain(testPath("foo.cpp"), CppAST
);
489 auto Symbols
= runFuzzyFind(Index
, "");
490 // Check foo is merged, foo in Cpp wins (as we see the definition there).
491 EXPECT_THAT(Symbols
, ElementsAre(AllOf(declURI("unittest:///foo.h"),
492 defURI("unittest:///foo.cpp"),
493 hasOrign(SymbolOrigin::Merge
))));
496 TEST(FileSymbolsTest
, CountReferencesNoRefSlabs
) {
497 FileSymbols
FS(IndexContents::All
);
498 FS
.update("f1", numSlab(1, 3), nullptr, nullptr, true);
499 FS
.update("f2", numSlab(1, 3), nullptr, nullptr, false);
501 runFuzzyFind(*FS
.buildIndex(IndexType::Light
, DuplicateHandling::Merge
),
503 UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)),
504 AllOf(qName("2"), numReferences(0u)),
505 AllOf(qName("3"), numReferences(0u))));
508 TEST(FileSymbolsTest
, CountReferencesWithRefSlabs
) {
509 FileSymbols
FS(IndexContents::All
);
510 FS
.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
512 FS
.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
514 FS
.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr,
516 FS
.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr,
518 FS
.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr,
520 FS
.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr,
523 runFuzzyFind(*FS
.buildIndex(IndexType::Light
, DuplicateHandling::Merge
),
525 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
526 AllOf(qName("2"), numReferences(1u)),
527 AllOf(qName("3"), numReferences(1u))));
530 TEST(FileIndexTest
, StalePreambleSymbolsDeleted
) {
533 File
.HeaderFilename
= "a.h";
535 File
.Filename
= "f1.cpp";
536 File
.HeaderCode
= "int a;";
537 auto AST
= File
.build();
538 M
.updatePreamble(testPath(File
.Filename
), /*Version=*/"null",
539 AST
.getASTContext(), AST
.getPreprocessor(),
540 AST
.getCanonicalIncludes());
541 EXPECT_THAT(runFuzzyFind(M
, ""), UnorderedElementsAre(qName("a")));
543 File
.Filename
= "f2.cpp";
544 File
.HeaderCode
= "int b;";
546 M
.updatePreamble(testPath(File
.Filename
), /*Version=*/"null",
547 AST
.getASTContext(), AST
.getPreprocessor(),
548 AST
.getCanonicalIncludes());
549 EXPECT_THAT(runFuzzyFind(M
, ""), UnorderedElementsAre(qName("b")));
552 // Verifies that concurrent calls to updateMain don't "lose" any updates.
553 TEST(FileIndexTest
, Threadsafety
) {
557 constexpr int Count
= 10;
559 // Set up workers to concurrently call updateMain() with separate files.
560 AsyncTaskRunner Pool
;
561 for (unsigned I
= 0; I
< Count
; ++I
) {
562 auto TU
= TestTU::withCode(llvm::formatv("int xxx{0};", I
).str());
563 TU
.Filename
= llvm::formatv("x{0}.c", I
).str();
564 Pool
.runAsync(TU
.Filename
, [&, Filename(testPath(TU
.Filename
)),
565 AST(TU
.build())]() mutable {
567 M
.updateMain(Filename
, AST
);
570 // On your marks, get set...
574 EXPECT_THAT(runFuzzyFind(M
, "xxx"), ::testing::SizeIs(Count
));
577 TEST(FileShardedIndexTest
, Sharding
) {
578 auto AHeaderUri
= URI::create(testPath("a.h")).toString();
579 auto BHeaderUri
= URI::create(testPath("b.h")).toString();
580 auto BSourceUri
= URI::create(testPath("b.cc")).toString();
582 auto Sym1
= symbol("1");
583 Sym1
.CanonicalDeclaration
.FileURI
= AHeaderUri
.c_str();
585 auto Sym2
= symbol("2");
586 Sym2
.CanonicalDeclaration
.FileURI
= BHeaderUri
.c_str();
587 Sym2
.Definition
.FileURI
= BSourceUri
.c_str();
589 auto Sym3
= symbol("3"); // not stored
593 SymbolSlab::Builder B
;
594 // Should be stored in only a.h
596 // Should be stored in both b.h and b.cc
598 IF
.Symbols
.emplace(std::move(B
).build());
601 // Should be stored in b.cc
602 IF
.Refs
.emplace(std::move(*refSlab(Sym1
.ID
, BSourceUri
.c_str())));
605 RelationSlab::Builder B
;
606 // Should be stored in a.h and b.h
607 B
.insert(Relation
{Sym1
.ID
, RelationKind::BaseOf
, Sym2
.ID
});
608 // Should be stored in a.h and b.h
609 B
.insert(Relation
{Sym2
.ID
, RelationKind::BaseOf
, Sym1
.ID
});
610 // Should be stored in a.h (where Sym1 is stored) even though
611 // the relation is dangling as Sym3 is unknown.
612 B
.insert(Relation
{Sym3
.ID
, RelationKind::BaseOf
, Sym1
.ID
});
613 IF
.Relations
.emplace(std::move(B
).build());
616 IF
.Sources
.emplace();
617 IncludeGraph
&IG
= *IF
.Sources
;
620 auto &Node
= IG
[BSourceUri
];
621 Node
.DirectIncludes
= {BHeaderUri
};
622 Node
.URI
= BSourceUri
;
626 auto &Node
= IG
[BHeaderUri
];
627 Node
.DirectIncludes
= {AHeaderUri
};
628 Node
.URI
= BHeaderUri
;
631 // a.h includes nothing.
632 auto &Node
= IG
[AHeaderUri
];
633 Node
.DirectIncludes
= {};
634 Node
.URI
= AHeaderUri
;
637 IF
.Cmd
= tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out");
639 FileShardedIndex
ShardedIndex(std::move(IF
));
640 ASSERT_THAT(ShardedIndex
.getAllSources(),
641 UnorderedElementsAre(AHeaderUri
, BHeaderUri
, BSourceUri
));
644 auto Shard
= ShardedIndex
.getShard(AHeaderUri
);
646 EXPECT_THAT(*Shard
->Symbols
, UnorderedElementsAre(qName("1")));
647 EXPECT_THAT(*Shard
->Refs
, IsEmpty());
650 UnorderedElementsAre(Relation
{Sym1
.ID
, RelationKind::BaseOf
, Sym2
.ID
},
651 Relation
{Sym2
.ID
, RelationKind::BaseOf
, Sym1
.ID
},
652 Relation
{Sym3
.ID
, RelationKind::BaseOf
, Sym1
.ID
}));
653 ASSERT_THAT(Shard
->Sources
->keys(), UnorderedElementsAre(AHeaderUri
));
654 EXPECT_THAT(Shard
->Sources
->lookup(AHeaderUri
).DirectIncludes
, IsEmpty());
655 EXPECT_TRUE(Shard
->Cmd
);
658 auto Shard
= ShardedIndex
.getShard(BHeaderUri
);
660 EXPECT_THAT(*Shard
->Symbols
, UnorderedElementsAre(qName("2")));
661 EXPECT_THAT(*Shard
->Refs
, IsEmpty());
664 UnorderedElementsAre(Relation
{Sym1
.ID
, RelationKind::BaseOf
, Sym2
.ID
},
665 Relation
{Sym2
.ID
, RelationKind::BaseOf
, Sym1
.ID
}));
666 ASSERT_THAT(Shard
->Sources
->keys(),
667 UnorderedElementsAre(BHeaderUri
, AHeaderUri
));
668 EXPECT_THAT(Shard
->Sources
->lookup(BHeaderUri
).DirectIncludes
,
669 UnorderedElementsAre(AHeaderUri
));
670 EXPECT_TRUE(Shard
->Cmd
);
673 auto Shard
= ShardedIndex
.getShard(BSourceUri
);
675 EXPECT_THAT(*Shard
->Symbols
, UnorderedElementsAre(qName("2")));
676 EXPECT_THAT(*Shard
->Refs
, UnorderedElementsAre(Pair(Sym1
.ID
, _
)));
677 EXPECT_THAT(*Shard
->Relations
, IsEmpty());
678 ASSERT_THAT(Shard
->Sources
->keys(),
679 UnorderedElementsAre(BSourceUri
, BHeaderUri
));
680 EXPECT_THAT(Shard
->Sources
->lookup(BSourceUri
).DirectIncludes
,
681 UnorderedElementsAre(BHeaderUri
));
682 EXPECT_TRUE(Shard
->Cmd
);
686 TEST(FileIndexTest
, Profile
) {
689 auto FileName
= testPath("foo.cpp");
690 auto AST
= TestTU::withHeaderCode("int a;").build();
691 FI
.updateMain(FileName
, AST
);
692 FI
.updatePreamble(FileName
, "v1", AST
.getASTContext(), AST
.getPreprocessor(),
693 AST
.getCanonicalIncludes());
695 llvm::BumpPtrAllocator Alloc
;
696 MemoryTree
MT(&Alloc
);
698 ASSERT_THAT(MT
.children(),
699 UnorderedElementsAre(Pair("preamble", _
), Pair("main_file", _
)));
701 ASSERT_THAT(MT
.child("preamble").children(),
702 UnorderedElementsAre(Pair("index", _
), Pair("slabs", _
)));
703 ASSERT_THAT(MT
.child("main_file").children(),
704 UnorderedElementsAre(Pair("index", _
), Pair("slabs", _
)));
706 ASSERT_THAT(MT
.child("preamble").child("index").total(), Gt(0U));
707 ASSERT_THAT(MT
.child("main_file").child("index").total(), Gt(0U));
710 TEST(FileSymbolsTest
, Profile
) {
711 FileSymbols
FS(IndexContents::All
);
712 FS
.update("f1", numSlab(1, 2), nullptr, nullptr, false);
713 FS
.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
714 FS
.update("f3", nullptr, nullptr,
715 relSlab({{SymbolID("1"), RelationKind::BaseOf
, SymbolID("2")}}),
717 llvm::BumpPtrAllocator Alloc
;
718 MemoryTree
MT(&Alloc
);
720 ASSERT_THAT(MT
.children(), UnorderedElementsAre(Pair("f1", _
), Pair("f2", _
),
722 EXPECT_THAT(MT
.child("f1").children(), ElementsAre(Pair("symbols", _
)));
723 EXPECT_THAT(MT
.child("f1").total(), Gt(0U));
724 EXPECT_THAT(MT
.child("f2").children(), ElementsAre(Pair("references", _
)));
725 EXPECT_THAT(MT
.child("f2").total(), Gt(0U));
726 EXPECT_THAT(MT
.child("f3").children(), ElementsAre(Pair("relations", _
)));
727 EXPECT_THAT(MT
.child("f3").total(), Gt(0U));
730 TEST(FileIndexTest
, MacrosFromMainFile
) {
733 TU
.Code
= "#pragma once\n#define FOO";
734 TU
.Filename
= "foo.h";
735 auto AST
= TU
.build();
736 Idx
.updateMain(testPath(TU
.Filename
), AST
);
738 auto Slab
= runFuzzyFind(Idx
, "");
739 auto &FooSymbol
= findSymbol(Slab
, "FOO");
740 EXPECT_TRUE(FooSymbol
.Flags
& Symbol::IndexedForCodeCompletion
);
744 } // namespace clangd