18 #include "clang/Frontend/CompilerInvocation.h" 19 #include "clang/Frontend/Utils.h" 20 #include "clang/Index/IndexSymbol.h" 21 #include "clang/Lex/Preprocessor.h" 22 #include "clang/Tooling/CompilationDatabase.h" 23 #include "gmock/gmock.h" 24 #include "gtest/gtest.h" 27 using ::testing::AllOf;
28 using ::testing::Contains;
29 using ::testing::ElementsAre;
30 using ::testing::IsEmpty;
31 using ::testing::Pair;
32 using ::testing::UnorderedElementsAre;
35 return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(),
36 arg.Location.End.line(), arg.Location.End.column()) ==
37 std::make_tuple(
Range.start.line,
Range.start.character,
40 MATCHER_P(FileURI, F,
"") {
return llvm::StringRef(arg.Location.FileURI) == F; }
42 return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U;
45 return llvm::StringRef(arg.Definition.FileURI) == U;
48 MATCHER_P(NumReferences, N,
"") {
return arg.References == N; }
49 MATCHER_P(hasOrign, O,
"") {
return bool(arg.Origin & O); }
54 ::testing::Matcher<const RefSlab &>
55 RefsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
56 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
59 Symbol
symbol(llvm::StringRef ID) {
66 std::unique_ptr<SymbolSlab> numSlab(
int Begin,
int End) {
68 for (
int i = Begin; i <= End; i++)
69 Slab.insert(
symbol(std::to_string(i)));
70 return llvm::make_unique<SymbolSlab>(std::move(Slab).build());
73 std::unique_ptr<RefSlab> refSlab(
const SymbolID &ID,
const char *
Path) {
76 R.Location.FileURI =
Path;
79 return llvm::make_unique<RefSlab>(std::move(Slab).build());
82 TEST(FileSymbolsTest, UpdateAndGet) {
86 FS.update(
"f1", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.cc"),
nullptr,
94 TEST(FileSymbolsTest, Overlap) {
96 FS.update(
"f1", numSlab(1, 3),
nullptr,
nullptr,
false);
97 FS.update(
"f2", numSlab(3, 5),
nullptr,
nullptr,
false);
104 TEST(FileSymbolsTest, MergeOverlap) {
106 auto OneSymboSlab = [](Symbol Sym) {
109 return llvm::make_unique<SymbolSlab>(std::move(S).build());
112 X1.CanonicalDeclaration.FileURI =
"file:///x1";
114 X2.Definition.FileURI =
"file:///x2";
116 FS.update(
"f1", OneSymboSlab(X1),
nullptr,
nullptr,
false);
117 FS.update(
"f2", OneSymboSlab(X2),
nullptr,
nullptr,
false);
121 UnorderedElementsAre(
122 AllOf(
QName(
"x"), DeclURI(
"file:///x1"), DefURI(
"file:///x2"))));
125 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
129 FS.update(
"f1", numSlab(1, 3), refSlab(ID,
"f1.cc"),
nullptr,
false);
136 FS.update(
"f1",
nullptr,
nullptr,
nullptr,
false);
147 void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
149 File.Filename = (Basename +
".cpp").str();
150 File.HeaderFilename = (Basename +
".h").str();
151 File.HeaderCode = Code;
152 auto AST = File.
build();
157 TEST(FileIndexTest, CustomizedURIScheme) {
159 update(M,
"f",
"class string {};");
161 EXPECT_THAT(
runFuzzyFind(M,
""), ElementsAre(DeclURI(
"unittest:///f.h")));
164 TEST(FileIndexTest, IndexAST) {
166 update(M,
"f1",
"namespace ns { void f() {} class X {}; }");
168 FuzzyFindRequest Req;
170 Req.Scopes = {
"ns::"};
172 UnorderedElementsAre(
QName(
"ns::f"),
QName(
"ns::X")));
175 TEST(FileIndexTest, NoLocal) {
177 update(M,
"f1",
"namespace ns { void f() { int local = 0; } class X {}; }");
184 TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
186 update(M,
"f1",
"namespace ns { void f() {} class X {}; }");
187 update(M,
"f2",
"namespace ns { void ff() {} class X {}; }");
189 FuzzyFindRequest Req;
190 Req.Scopes = {
"ns::"};
193 UnorderedElementsAre(
QName(
"ns::f"),
QName(
"ns::X"),
QName(
"ns::ff")));
196 TEST(FileIndexTest, ClassMembers) {
198 update(M,
"f1",
"class X { static int m1; int m2; static void f(); };");
205 TEST(FileIndexTest, IncludeCollected) {
209 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
212 EXPECT_THAT(
Symbols, ElementsAre(_));
213 EXPECT_THAT(
Symbols.
begin()->IncludeHeaders.front().IncludeHeader,
214 "<the/good/header.h>");
217 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
219 TU.HeaderCode =
"class Foo{};";
220 TU.HeaderFilename =
"algorithm";
223 EXPECT_THAT(
Symbols, ElementsAre(_));
224 EXPECT_THAT(
Symbols.
begin()->IncludeHeaders.front().IncludeHeader,
228 TEST(FileIndexTest, TemplateParamsInLabel) {
234 template <class Ty, class Arg> 235 vector<Ty> make_vector(Arg A) {} 239 update(M, "f", Source);
243 UnorderedElementsAre(
QName(
"vector"),
QName(
"make_vector")));
245 Symbol Vector = *It++;
246 Symbol MakeVector = *It++;
247 if (MakeVector.Name ==
"vector")
248 std::swap(MakeVector, Vector);
250 EXPECT_EQ(Vector.Signature,
"<class Ty>");
251 EXPECT_EQ(Vector.CompletionSnippetSuffix,
"<${1:class Ty}>");
253 EXPECT_EQ(MakeVector.Signature,
"<class Ty>(Arg A)");
254 EXPECT_EQ(MakeVector.CompletionSnippetSuffix,
"<${1:class Ty}>(${2:Arg A})");
257 TEST(FileIndexTest, RebuildWithPreamble) {
262 PI.CompileCommand.Directory =
testRoot();
263 PI.CompileCommand.Filename = FooCpp;
264 PI.CompileCommand.CommandLine = {
"clang",
"-xc++", FooCpp};
266 llvm::StringMap<std::string>
Files;
269 namespace ns_in_header { 270 int func_in_header(); 277 namespace ns_in_source { 278 int func_in_source(); 286 bool IndexUpdated =
false;
288 FooCpp, *CI,
nullptr, tooling::CompileCommand(), PI,
290 [&](ASTContext &
Ctx, std::shared_ptr<Preprocessor> PP,
291 const CanonicalIncludes &CanonIncludes) {
292 EXPECT_FALSE(IndexUpdated) <<
"Expected only a single index update";
294 Index.updatePreamble(FooCpp, Ctx, std::move(PP), CanonIncludes);
296 ASSERT_TRUE(IndexUpdated);
300 FuzzyFindRequest Req;
302 Req.Scopes = {
"",
"ns_in_header::"};
305 UnorderedElementsAre(
QName(
"ns_in_header"),
306 QName(
"ns_in_header::func_in_header")));
310 const char *HeaderCode =
"class Foo {};";
311 Annotations MainCode(R
"cpp( 321 Request.IDs = {Foo.ID};
326 Test.HeaderCode = HeaderCode;
327 Test.Code = MainCode.code();
328 Test.Filename =
"test.cc";
329 auto AST = Test.
build();
330 Index.updateMain(Test.Filename, AST);
333 Test2.HeaderCode = HeaderCode;
334 Test2.Code = MainCode.code();
335 Test2.Filename =
"test2.cc";
337 Index.updateMain(Test2.Filename, AST);
339 EXPECT_THAT(
getRefs(Index, Foo.ID),
340 RefsAre({AllOf(RefRange(MainCode.range(
"foo")),
341 FileURI(
"unittest:///test.cc")),
342 AllOf(RefRange(MainCode.range(
"foo")),
343 FileURI(
"unittest:///test2.cc"))}));
346 TEST(FileIndexTest, CollectMacros) {
348 update(M,
"f",
"#define CLANGD 1");
354 TU.Filename =
"f.cpp";
355 TU.HeaderFilename =
"f.h";
356 TU.HeaderCode =
"class A {}; class B : public A {};";
357 auto AST = TU.
build();
363 RelationsRequest Req;
364 Req.Subjects.insert(A);
365 Req.Predicate = index::SymbolRole::RelationBaseOf;
366 Index.relations(Req, [&](
const SymbolID &,
const Symbol &) { ++
Results; });
367 EXPECT_EQ(Results, 1u);
370 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
372 TU.HeaderCode =
"class Foo{};";
373 Annotations Main(R
"cpp( 379 TU.Code = Main.code(); 380 auto AST = TU.
build();
382 Index.updateMain(
testPath(TU.Filename), AST);
387 RefsAre({RefRange(Main.range())}));
390 TEST(FileIndexTest, MergeMainFileSymbols) {
391 const char* CommonHeader =
"void foo();";
394 Cpp.Filename =
"foo.cpp";
395 Cpp.HeaderFilename =
"foo.h";
396 Cpp.HeaderCode = CommonHeader;
399 auto HeaderAST = Header.build();
400 auto CppAST = Cpp.build();
401 Index.updateMain(
testPath(
"foo.h"), HeaderAST);
402 Index.updateMain(
testPath(
"foo.cpp"), CppAST);
406 EXPECT_THAT(
Symbols, ElementsAre(AllOf(DeclURI(
"unittest:///foo.h"),
407 DefURI(
"unittest:///foo.cpp"),
411 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
413 FS.update(
"f1", numSlab(1, 3),
nullptr,
nullptr,
true);
414 FS.update(
"f2", numSlab(1, 3),
nullptr,
nullptr,
false);
418 UnorderedElementsAre(AllOf(
QName(
"1"), NumReferences(0u)),
419 AllOf(
QName(
"2"), NumReferences(0u)),
420 AllOf(
QName(
"3"), NumReferences(0u))));
423 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
425 FS.update(
"f1cpp", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.cpp"),
nullptr,
427 FS.update(
"f1h", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.h"),
nullptr,
429 FS.update(
"f2cpp", numSlab(1, 3), refSlab(
SymbolID(
"2"),
"f2.cpp"),
nullptr,
431 FS.update(
"f2h", numSlab(1, 3), refSlab(
SymbolID(
"2"),
"f2.h"),
nullptr,
433 FS.update(
"f3cpp", numSlab(1, 3), refSlab(
SymbolID(
"3"),
"f3.cpp"),
nullptr,
435 FS.update(
"f3h", numSlab(1, 3), refSlab(
SymbolID(
"3"),
"f3.h"),
nullptr,
440 UnorderedElementsAre(AllOf(
QName(
"1"), NumReferences(1u)),
441 AllOf(
QName(
"2"), NumReferences(1u)),
442 AllOf(
QName(
"3"), NumReferences(1u))));
Symbol symbol(llvm::StringRef QName)
::testing::Matcher< const RefSlab & > RefsAre(std::vector<::testing::Matcher< Ref >> Matchers)
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
std::vector< CodeCompletionResult > Results
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
SymbolID ID
The ID of the symbol.
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation &CI, std::shared_ptr< const PreambleData > OldPreamble, const tooling::CompileCommand &OldCompileCommand, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback)
Rebuild the preamble for the new inputs unless the old one can be reused.
const CanonicalIncludes & getCanonicalIncludes() const
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
std::shared_ptr< Preprocessor > getPreprocessorPtr()
std::string Path
A typedef to represent a file path.
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs)
Builds compiler invocation that could be used to build AST or preamble.
MATCHER_P(RefRange, Range, "")
static TestTU withCode(llvm::StringRef Code)
CodeCompletionBuilder Builder
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
const_iterator begin() const
CharSourceRange Range
SourceRange for the file name.
std::array< uint8_t, 20 > SymbolID
llvm::StringMap< std::string > Files
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
const SymbolIndex * Index