19 #include "clang/Frontend/CompilerInvocation.h" 20 #include "clang/Frontend/Utils.h" 21 #include "clang/Index/IndexSymbol.h" 22 #include "clang/Lex/Preprocessor.h" 23 #include "clang/Tooling/CompilationDatabase.h" 24 #include "gmock/gmock.h" 25 #include "gtest/gtest.h" 28 using ::testing::AllOf;
29 using ::testing::Contains;
30 using ::testing::ElementsAre;
31 using ::testing::IsEmpty;
32 using ::testing::Pair;
33 using ::testing::UnorderedElementsAre;
36 return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(),
37 arg.Location.End.line(), arg.Location.End.column()) ==
38 std::make_tuple(
Range.start.line,
Range.start.character,
41 MATCHER_P(FileURI, F,
"") {
return llvm::StringRef(arg.Location.FileURI) == F; }
43 return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U;
46 return llvm::StringRef(arg.Definition.FileURI) == U;
49 MATCHER_P(NumReferences, N,
"") {
return arg.References == N; }
50 MATCHER_P(hasOrign, O,
"") {
return bool(arg.Origin & O); }
55 ::testing::Matcher<const RefSlab &>
56 RefsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
57 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
60 Symbol
symbol(llvm::StringRef ID) {
67 std::unique_ptr<SymbolSlab> numSlab(
int Begin,
int End) {
69 for (
int i = Begin; i <= End; i++)
70 Slab.insert(
symbol(std::to_string(i)));
71 return std::make_unique<SymbolSlab>(std::move(Slab).build());
74 std::unique_ptr<RefSlab> refSlab(
const SymbolID &ID,
const char *
Path) {
77 R.Location.FileURI =
Path;
80 return std::make_unique<RefSlab>(std::move(Slab).build());
83 TEST(FileSymbolsTest, UpdateAndGet) {
87 FS.update(
"f1", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.cc"),
nullptr,
95 TEST(FileSymbolsTest, Overlap) {
97 FS.update(
"f1", numSlab(1, 3),
nullptr,
nullptr,
false);
98 FS.update(
"f2", numSlab(3, 5),
nullptr,
nullptr,
false);
105 TEST(FileSymbolsTest, MergeOverlap) {
107 auto OneSymboSlab = [](Symbol Sym) {
110 return std::make_unique<SymbolSlab>(std::move(S).build());
113 X1.CanonicalDeclaration.FileURI =
"file:///x1";
115 X2.Definition.FileURI =
"file:///x2";
117 FS.update(
"f1", OneSymboSlab(X1),
nullptr,
nullptr,
false);
118 FS.update(
"f2", OneSymboSlab(X2),
nullptr,
nullptr,
false);
122 UnorderedElementsAre(
123 AllOf(
QName(
"x"), DeclURI(
"file:///x1"), DefURI(
"file:///x2"))));
126 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
130 FS.update(
"f1", numSlab(1, 3), refSlab(ID,
"f1.cc"),
nullptr,
false);
137 FS.update(
"f1",
nullptr,
nullptr,
nullptr,
false);
148 void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef
Code) {
150 File.Filename = (Basename +
".cpp").str();
151 File.HeaderFilename = (Basename +
".h").str();
152 File.HeaderCode =
Code;
153 auto AST = File.
build();
158 TEST(FileIndexTest, CustomizedURIScheme) {
160 update(M,
"f",
"class string {};");
162 EXPECT_THAT(
runFuzzyFind(M,
""), ElementsAre(DeclURI(
"unittest:///f.h")));
165 TEST(FileIndexTest, IndexAST) {
167 update(M,
"f1",
"namespace ns { void f() {} class X {}; }");
169 FuzzyFindRequest Req;
171 Req.Scopes = {
"ns::"};
173 UnorderedElementsAre(
QName(
"ns::f"),
QName(
"ns::X")));
176 TEST(FileIndexTest, NoLocal) {
178 update(M,
"f1",
"namespace ns { void f() { int local = 0; } class X {}; }");
185 TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
187 update(M,
"f1",
"namespace ns { void f() {} class X {}; }");
188 update(M,
"f2",
"namespace ns { void ff() {} class X {}; }");
190 FuzzyFindRequest Req;
191 Req.Scopes = {
"ns::"};
194 UnorderedElementsAre(
QName(
"ns::f"),
QName(
"ns::X"),
QName(
"ns::ff")));
197 TEST(FileIndexTest, ClassMembers) {
199 update(M,
"f1",
"class X { static int m1; int m2; static void f(); };");
206 TEST(FileIndexTest, IncludeCollected) {
210 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
213 EXPECT_THAT(
Symbols, ElementsAre(_));
214 EXPECT_THAT(
Symbols.
begin()->IncludeHeaders.front().IncludeHeader,
215 "<the/good/header.h>");
218 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
220 TU.HeaderCode =
"class Foo{};";
221 TU.HeaderFilename =
"algorithm";
224 EXPECT_THAT(
Symbols, ElementsAre(_));
225 EXPECT_THAT(
Symbols.
begin()->IncludeHeaders.front().IncludeHeader,
229 TEST(FileIndexTest, TemplateParamsInLabel) {
235 template <class Ty, class Arg> 236 vector<Ty> make_vector(Arg A) {} 240 update(M, "f", Source);
244 UnorderedElementsAre(
QName(
"vector"),
QName(
"make_vector")));
246 Symbol Vector = *It++;
247 Symbol MakeVector = *It++;
248 if (MakeVector.Name ==
"vector")
249 std::swap(MakeVector, Vector);
251 EXPECT_EQ(Vector.Signature,
"<class Ty>");
252 EXPECT_EQ(Vector.CompletionSnippetSuffix,
"<${1:class Ty}>");
254 EXPECT_EQ(MakeVector.Signature,
"<class Ty>(Arg A)");
255 EXPECT_EQ(MakeVector.CompletionSnippetSuffix,
"<${1:class Ty}>(${2:Arg A})");
258 TEST(FileIndexTest, RebuildWithPreamble) {
263 PI.CompileCommand.Directory =
testRoot();
264 PI.CompileCommand.Filename = FooCpp;
265 PI.CompileCommand.CommandLine = {
"clang",
"-xc++", FooCpp};
267 llvm::StringMap<std::string>
Files;
270 namespace ns_in_header { 271 int func_in_header(); 278 namespace ns_in_source { 279 int func_in_source(); 288 bool IndexUpdated =
false;
290 FooCpp, *CI,
nullptr, tooling::CompileCommand(), PI,
292 [&](ASTContext &
Ctx, std::shared_ptr<Preprocessor>
PP,
293 const CanonicalIncludes &CanonIncludes) {
294 EXPECT_FALSE(IndexUpdated) <<
"Expected only a single index update";
296 Index.updatePreamble(FooCpp, Ctx, std::move(PP), CanonIncludes);
298 ASSERT_TRUE(IndexUpdated);
302 FuzzyFindRequest Req;
304 Req.Scopes = {
"",
"ns_in_header::"};
307 UnorderedElementsAre(
QName(
"ns_in_header"),
308 QName(
"ns_in_header::func_in_header")));
312 const char *HeaderCode =
"class Foo {};";
313 Annotations MainCode(R
"cpp( 323 Request.IDs = {Foo.ID};
328 Test.HeaderCode = HeaderCode;
329 Test.Code = MainCode.code();
330 Test.Filename =
"test.cc";
331 auto AST = Test.
build();
332 Index.updateMain(Test.Filename, AST);
335 Test2.HeaderCode = HeaderCode;
336 Test2.Code = MainCode.code();
337 Test2.Filename =
"test2.cc";
339 Index.updateMain(Test2.Filename, AST);
341 EXPECT_THAT(
getRefs(Index, Foo.ID),
342 RefsAre({AllOf(RefRange(MainCode.range(
"foo")),
343 FileURI(
"unittest:///test.cc")),
344 AllOf(RefRange(MainCode.range(
"foo")),
345 FileURI(
"unittest:///test2.cc"))}));
348 TEST(FileIndexTest, MacroRefs) {
349 Annotations HeaderCode(R
"cpp( 350 #define $def1[[HEADER_MACRO]](X) (X+1) 352 Annotations MainCode(R"cpp( 353 #define $def2[[MAINFILE_MACRO]](X) (X+1) 355 int a = $ref1[[HEADER_MACRO]](2); 356 int b = $ref2[[MAINFILE_MACRO]](1); 363 Test.HeaderCode = HeaderCode.code();
364 Test.Code = MainCode.code();
365 Test.Filename =
"test.cc";
366 auto AST = Test.
build();
367 Index.updateMain(Test.Filename, AST);
369 auto HeaderMacro =
findSymbol(Test.headerSymbols(),
"HEADER_MACRO");
370 EXPECT_THAT(
getRefs(Index, HeaderMacro.ID),
371 RefsAre({AllOf(RefRange(MainCode.range(
"ref1")),
372 FileURI(
"unittest:///test.cc"))}));
374 auto MainFileMacro =
findSymbol(Test.headerSymbols(),
"MAINFILE_MACRO");
375 EXPECT_THAT(
getRefs(Index, MainFileMacro.ID),
376 RefsAre({AllOf(RefRange(MainCode.range(
"def2")),
377 FileURI(
"unittest:///test.cc")),
378 AllOf(RefRange(MainCode.range(
"ref2")),
379 FileURI(
"unittest:///test.cc"))}));
382 TEST(FileIndexTest, CollectMacros) {
384 update(M,
"f",
"#define CLANGD 1");
388 TEST(FileIndexTest, Relations) {
390 TU.Filename =
"f.cpp";
391 TU.HeaderFilename =
"f.h";
392 TU.HeaderCode =
"class A {}; class B : public A {};";
393 auto AST = TU.
build();
399 RelationsRequest Req;
400 Req.Subjects.insert(A);
402 Index.relations(Req, [&](
const SymbolID &,
const Symbol &) { ++
Results; });
403 EXPECT_EQ(Results, 1u);
406 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
408 TU.HeaderCode =
"class Foo{};";
409 Annotations Main(R
"cpp( 415 TU.Code = Main.code(); 416 auto AST = TU.
build();
418 Index.updateMain(
testPath(TU.Filename), AST);
423 RefsAre({RefRange(Main.range())}));
426 TEST(FileIndexTest, MergeMainFileSymbols) {
427 const char* CommonHeader =
"void foo();";
430 Cpp.Filename =
"foo.cpp";
431 Cpp.HeaderFilename =
"foo.h";
432 Cpp.HeaderCode = CommonHeader;
435 auto HeaderAST = Header.build();
436 auto CppAST = Cpp.build();
437 Index.updateMain(
testPath(
"foo.h"), HeaderAST);
438 Index.updateMain(
testPath(
"foo.cpp"), CppAST);
442 EXPECT_THAT(
Symbols, ElementsAre(AllOf(DeclURI(
"unittest:///foo.h"),
443 DefURI(
"unittest:///foo.cpp"),
447 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
449 FS.update(
"f1", numSlab(1, 3),
nullptr,
nullptr,
true);
450 FS.update(
"f2", numSlab(1, 3),
nullptr,
nullptr,
false);
454 UnorderedElementsAre(AllOf(
QName(
"1"), NumReferences(0u)),
455 AllOf(
QName(
"2"), NumReferences(0u)),
456 AllOf(
QName(
"3"), NumReferences(0u))));
459 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
461 FS.update(
"f1cpp", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.cpp"),
nullptr,
463 FS.update(
"f1h", numSlab(1, 3), refSlab(
SymbolID(
"1"),
"f1.h"),
nullptr,
465 FS.update(
"f2cpp", numSlab(1, 3), refSlab(
SymbolID(
"2"),
"f2.cpp"),
nullptr,
467 FS.update(
"f2h", numSlab(1, 3), refSlab(
SymbolID(
"2"),
"f2.h"),
nullptr,
469 FS.update(
"f3cpp", numSlab(1, 3), refSlab(
SymbolID(
"3"),
"f3.cpp"),
nullptr,
471 FS.update(
"f3h", numSlab(1, 3), refSlab(
SymbolID(
"3"),
"f3.h"),
nullptr,
476 UnorderedElementsAre(AllOf(
QName(
"1"), NumReferences(1u)),
477 AllOf(
QName(
"2"), NumReferences(1u)),
478 AllOf(
QName(
"3"), NumReferences(1u))));
Symbol symbol(llvm::StringRef QName)
::testing::Matcher< const RefSlab & > RefsAre(std::vector<::testing::Matcher< Ref >> Matchers)
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)
Build a preamble for the new inputs unless an 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)
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::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.
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
std::array< uint8_t, 20 > SymbolID
llvm::StringMap< std::string > Files
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
const SymbolIndex * Index