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 "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"
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
); }
63 MATCHER_P(includeHeader
, P
, "") {
64 return (arg
.IncludeHeaders
.size() == 1) &&
65 (arg
.IncludeHeaders
.begin()->IncludeHeader
== P
);
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
) {
78 Sym
.ID
= SymbolID(ID
);
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
;
93 R
.Location
.FileURI
= Path
;
94 R
.Kind
= RefKind::Reference
;
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,
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
;
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
})
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
);
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
) {
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
) {
184 update(M
, "f", "class string {};");
186 EXPECT_THAT(runFuzzyFind(M
, ""), ElementsAre(declURI("unittest:///f.h")));
189 TEST(FileIndexTest
, IndexAST
) {
191 update(M
, "f1", "namespace ns { void f() {} class X {}; }");
193 FuzzyFindRequest Req
;
195 Req
.Scopes
= {"ns::"};
196 EXPECT_THAT(runFuzzyFind(M
, Req
),
197 UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
200 TEST(FileIndexTest
, NoLocal
) {
202 update(M
, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
206 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
209 TEST(FileIndexTest
, IndexMultiASTAndDeduplicate
) {
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::"};
217 runFuzzyFind(M
, Req
),
218 UnorderedElementsAre(qName("ns::f"), qName("ns::X"), qName("ns::ff")));
221 TEST(FileIndexTest
, ClassMembers
) {
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"),
230 TEST(FileIndexTest
, IncludeCollected
) {
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
) {
246 File
.Code
= R
"cpp(#pragma once
247 #include "exporter
.h
"
249 File
.HeaderFilename
= "exporter.h";
250 File
.HeaderCode
= R
"cpp(#pragma once
251 #include "private.h
" // IWYU pragma: export
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
, "");
262 UnorderedElementsAre(AllOf(
264 includeHeader(URI::create(testPath(File
.HeaderFilename
)).toString()),
265 declURI(URI::create(testPath("private.h")).toString()))));
268 TEST(FileIndexTest
, HasSystemHeaderMappingsInPreamble
) {
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
,
279 TEST(FileIndexTest
, TemplateParamsInLabel
) {
280 auto *Source
= R
"cpp(
285 template <class Ty, class Arg>
286 vector<Ty> make_vector(Arg A) {}
290 update(M
, "f", Source
);
292 auto Symbols
= runFuzzyFind(M
, "");
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.
313 PI
.CompileCommand
.Directory
= testRoot();
314 PI
.CompileCommand
.Filename
= FooCpp
;
315 PI
.CompileCommand
.CommandLine
= {"clang", "-xc++", FooCpp
};
318 FS
.Files
[FooCpp
] = "";
319 FS
.Files
[FooH
] = R
"cpp(
320 namespace ns_in_header {
321 int func_in_header();
328 namespace ns_in_source {
329 int func_in_source();
334 IgnoreDiagnostics IgnoreDiags
;
335 auto CI
= buildCompilerInvocation(PI
, IgnoreDiags
);
338 bool IndexUpdated
= false;
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";
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
354 FuzzyFindRequest Req
;
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(
372 findSymbol(TestTU::withHeaderCode(HeaderCode
).headerSymbols(), "Foo");
375 Request
.IDs
= {Foo
.ID
};
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
);
387 Test2
.HeaderCode
= HeaderCode
;
388 Test2
.Code
= std::string(MainCode
.code());
389 Test2
.Filename
= "test2.cc";
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)
404 Annotations
MainCode(R
"cpp(
405 #define $def2[[MAINFILE_MACRO]](X) (X+1)
407 int a = $ref1[[HEADER_MACRO]](2);
408 int b = $ref2[[MAINFILE_MACRO]](1);
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
) {
436 update(M
, "f", "#define CLANGD 1");
437 EXPECT_THAT(runFuzzyFind(M
, ""), Contains(qName("CLANGD")));
440 TEST(FileIndexTest
, Relations
) {
442 TU
.Filename
= "f.cpp";
443 TU
.HeaderFilename
= "f.h";
444 TU
.HeaderCode
= "class A {}; class B : public A {};";
445 auto AST
= TU
.build();
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(
464 class A : public Base {};
466 Workspace
.addMainFile("B.cpp", R
"cpp(
468 class B : public Base {};
471 auto Index
= Workspace
.index();
472 FuzzyFindRequest FFReq
;
473 FFReq
.Query
= "Base";
474 FFReq
.AnyScope
= true;
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
) {
488 TU
.HeaderCode
= "class Foo{};";
489 Annotations
Main(R
"cpp(
494 TU
.Code
= std::string(Main
.code());
495 auto AST
= TU
.build();
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
;
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);
531 runFuzzyFind(*FS
.buildIndex(IndexType::Light
, DuplicateHandling::Merge
),
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,
542 FS
.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
544 FS
.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr,
546 FS
.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr,
548 FS
.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr,
550 FS
.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr,
553 runFuzzyFind(*FS
.buildIndex(IndexType::Light
, DuplicateHandling::Merge
),
555 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
556 AllOf(qName("2"), numReferences(1u)),
557 AllOf(qName("3"), numReferences(1u))));
560 TEST(FileIndexTest
, StalePreambleSymbolsDeleted
) {
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;";
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
) {
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 {
597 M
.updateMain(Filename
, AST
);
600 // On your marks, get set...
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
623 SymbolSlab::Builder B
;
624 // Should be stored in only a.h
626 // Should be stored in both b.h and b.cc
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
;
650 auto &Node
= IG
[BSourceUri
];
651 Node
.DirectIncludes
= {BHeaderUri
};
652 Node
.URI
= BSourceUri
;
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
);
676 EXPECT_THAT(*Shard
->Symbols
, UnorderedElementsAre(qName("1")));
677 EXPECT_THAT(*Shard
->Refs
, IsEmpty());
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
);
690 EXPECT_THAT(*Shard
->Symbols
, UnorderedElementsAre(qName("2")));
691 EXPECT_THAT(*Shard
->Refs
, IsEmpty());
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
);
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
) {
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
);
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")}}),
747 llvm::BumpPtrAllocator Alloc
;
748 MemoryTree
MT(&Alloc
);
750 ASSERT_THAT(MT
.children(), UnorderedElementsAre(Pair("f1", _
), Pair("f2", _
),
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
) {
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
);
774 } // namespace clangd