13 #include "gmock/gmock.h" 14 #include "gtest/gtest.h" 21 using ::testing::AllOf;
22 using ::testing::ElementsAre;
23 using ::testing::ElementsAreArray;
24 using ::testing::Field;
25 using ::testing::IsEmpty;
26 using ::testing::UnorderedElementsAre;
28 class IgnoreDiagnostics :
public DiagnosticsConsumer {
30 std::vector<Diag> Diagnostics)
override {}
35 if (arg.containerName.empty())
36 return arg.name ==
Name;
37 return (arg.containerName +
"::" + arg.name) ==
Name;
39 MATCHER_P(WithName, N,
"") {
return arg.name == N; }
45 template <
class... ChildMatchers>
46 ::testing::Matcher<DocumentSymbol>
Children(ChildMatchers... ChildrenM) {
50 ClangdServer::Options optsForTests() {
52 ServerOpts.WorkspaceRoot =
testRoot();
53 ServerOpts.BuildDynamicSymbolIndex =
true;
57 class WorkspaceSymbolsTest :
public ::testing::Test {
59 WorkspaceSymbolsTest()
63 CDB.ExtraClangFlags = {
"-xc++"};
68 MockCompilationDatabase
CDB;
73 std::vector<SymbolInformation> getSymbols(llvm::StringRef Query) {
74 EXPECT_TRUE(Server.blockUntilIdleForTest()) <<
"Waiting for preamble";
76 EXPECT_TRUE(
bool(SymbolInfos)) <<
"workspaceSymbols returned an error";
83 Server.addDocument(
Path, Contents);
89 TEST_F(WorkspaceSymbolsTest, Macros) {
90 addFile(
"foo.cpp", R
"cpp( 97 EXPECT_THAT(getSymbols(
"macro"),
102 addFile(
"foo.cpp", R
"cpp( 103 void test(int FirstParam, int SecondParam) { 104 struct LocalClass {}; 107 EXPECT_THAT(getSymbols("l"), IsEmpty());
108 EXPECT_THAT(getSymbols(
"p"), IsEmpty());
112 addFile(
"foo.h", R
"cpp( 117 struct GlobalStruct {};)cpp"); 118 addFile("foo.cpp", R
"cpp( 121 EXPECT_THAT(getSymbols("global"),
122 UnorderedElementsAre(
129 addFile(
"foo.h", R
"cpp( 132 } UnnamedStruct;)cpp"); 133 addFile("foo.cpp", R
"cpp( 136 EXPECT_THAT(getSymbols("UnnamedStruct"),
137 ElementsAre(AllOf(
QName(
"UnnamedStruct"),
139 EXPECT_THAT(getSymbols(
"InUnnamed"),
140 ElementsAre(AllOf(
QName(
"(anonymous struct)::InUnnamed"),
144 TEST_F(WorkspaceSymbolsTest, InMainFile) {
145 addFile(
"foo.cpp", R
"cpp( 149 EXPECT_THAT(getSymbols("test"), ElementsAre(
QName(
"test"),
QName(
"test2")));
152 TEST_F(WorkspaceSymbolsTest, Namespaces) {
153 addFile(
"foo.h", R
"cpp( 161 addFile("foo.cpp", R
"cpp( 164 EXPECT_THAT(getSymbols("a"),
165 UnorderedElementsAre(
QName(
"ans1"),
QName(
"ans1::ai1"),
167 QName(
"ans1::ans2::ai2")));
168 EXPECT_THAT(getSymbols(
"::"), ElementsAre(
QName(
"ans1")));
169 EXPECT_THAT(getSymbols(
"::a"), ElementsAre(
QName(
"ans1")));
170 EXPECT_THAT(getSymbols(
"ans1::"),
171 UnorderedElementsAre(
QName(
"ans1::ai1"),
QName(
"ans1::ans2")));
172 EXPECT_THAT(getSymbols(
"::ans1"), ElementsAre(
QName(
"ans1")));
173 EXPECT_THAT(getSymbols(
"::ans1::"),
174 UnorderedElementsAre(
QName(
"ans1::ai1"),
QName(
"ans1::ans2")));
175 EXPECT_THAT(getSymbols(
"::ans1::ans2"), ElementsAre(
QName(
"ans1::ans2")));
176 EXPECT_THAT(getSymbols(
"::ans1::ans2::"),
177 ElementsAre(
QName(
"ans1::ans2::ai2")));
180 TEST_F(WorkspaceSymbolsTest, AnonymousNamespace) {
181 addFile(
"foo.cpp", R
"cpp( 186 EXPECT_THAT(getSymbols("test"), ElementsAre(
QName(
"test")));
189 TEST_F(WorkspaceSymbolsTest, MultiFile) {
190 addFile(
"foo.h", R
"cpp( 194 addFile("foo2.h", R
"cpp( 198 addFile("foo.cpp", R
"cpp( 202 EXPECT_THAT(getSymbols("foo"),
203 UnorderedElementsAre(
QName(
"foo"),
QName(
"foo2")));
206 TEST_F(WorkspaceSymbolsTest, GlobalNamespaceQueries) {
207 addFile(
"foo.h", R
"cpp( 218 addFile("foo.cpp", R
"cpp( 221 EXPECT_THAT(getSymbols("::"),
222 UnorderedElementsAre(
226 EXPECT_THAT(getSymbols(
":"), IsEmpty());
227 EXPECT_THAT(getSymbols(
""), IsEmpty());
231 addFile(
"foo.h", R
"cpp( 253 addFile("foo.cpp", R
"cpp( 256 EXPECT_THAT(getSymbols("Red"), ElementsAre(
QName(
"Red")));
257 EXPECT_THAT(getSymbols(
"::Red"), ElementsAre(
QName(
"Red")));
258 EXPECT_THAT(getSymbols(
"Green"), ElementsAre(
QName(
"Green")));
259 EXPECT_THAT(getSymbols(
"Green"), ElementsAre(
QName(
"Green")));
260 EXPECT_THAT(getSymbols(
"Color2::Yellow"),
261 ElementsAre(
QName(
"Color2::Yellow")));
262 EXPECT_THAT(getSymbols(
"Yellow"), ElementsAre(
QName(
"Color2::Yellow")));
264 EXPECT_THAT(getSymbols(
"ns::Black"), ElementsAre(
QName(
"ns::Black")));
265 EXPECT_THAT(getSymbols(
"ns::Blue"), ElementsAre(
QName(
"ns::Blue")));
266 EXPECT_THAT(getSymbols(
"ns::Color4::White"),
267 ElementsAre(
QName(
"ns::Color4::White")));
271 addFile(
"foo.h", R
"cpp( 275 addFile("foo.cpp", R
"cpp( 278 EXPECT_THAT(getSymbols("::"), ElementsAre(
QName(
"func"),
QName(
"ns")));
281 TEST_F(WorkspaceSymbolsTest, WithLimit) {
282 addFile(
"foo.h", R
"cpp( 286 addFile("foo.cpp", R
"cpp( 290 EXPECT_THAT(getSymbols(
"foo"),
291 UnorderedElementsAre(
296 EXPECT_THAT(getSymbols(
"foo"), ElementsAre(
QName(
"foo")));
299 TEST_F(WorkspaceSymbolsTest, TempSpecs) {
300 addFile(
"foo.h", R
"cpp( 301 template <typename T, typename U, int X = 5> class Foo {}; 302 template <typename T> class Foo<int, T> {}; 303 template <> class Foo<bool, int> {}; 304 template <> class Foo<bool, int, 3> {}; 309 UnorderedElementsAre(
317 class DocumentSymbolsTest :
public ::testing::Test {
319 DocumentSymbolsTest()
328 std::vector<DocumentSymbol> getSymbols(
PathRef File) {
331 EXPECT_TRUE(
bool(SymbolInfos)) <<
"documentSymbols returned an error";
335 void addFile(llvm::StringRef FilePath, llvm::StringRef
Contents) {
342 TEST_F(DocumentSymbolsTest, BasicSymbols) {
343 std::string FilePath =
testPath(
"foo.cpp");
352 Foo& operator=(const Foo&); 363 static const int KInt = 2; 364 const char* kStr = "123"; 371 using int32_t = int32; 387 addFile(FilePath, Main.code()); 389 getSymbols(FilePath), 395 AllOf(WithName(
"Foo"),
399 AllOf(WithName(
"operator="),
401 AllOf(WithName(
"~Foo"),
431 TEST_F(DocumentSymbolsTest, DeclarationDefinition) {
432 std::string FilePath =
testPath(
"foo.cpp");
437 void Foo::$def[[f]]() { 441 addFile(FilePath, Main.code()); 443 getSymbols(FilePath), 447 SymNameRange(Main.
range(
"decl"))))),
449 SymNameRange(Main.
range(
"def")))));
452 TEST_F(DocumentSymbolsTest, ExternSymbol) {
453 std::string FilePath =
testPath(
"foo.cpp");
457 addFile(FilePath, R"cpp( 461 EXPECT_THAT(getSymbols(FilePath), IsEmpty()); 465 std::string FilePath = testPath("foo.cpp");
468 void test(int FirstParam, int SecondParam) { 469 struct LocalClass {}; 472 EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
476 std::string FilePath =
testPath(
"foo.h");
484 getSymbols(FilePath), 487 Children(AllOf(WithName(
"InUnnamed"),
493 TEST_F(DocumentSymbolsTest, InHeaderFile) {
498 std::string FilePath = testPath("foo.h");
499 addFile(FilePath, R
"cpp( 507 EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
511 std::string FilePath =
testPath(
"foo.cpp");
512 addFile(FilePath, R
"( 513 template <class T> struct Tmpl {T x = 0;}; 514 template <> struct Tmpl<int> { 517 extern template struct Tmpl<float>; 518 template struct Tmpl<double>; 520 template <class T, class U, class Z = float> 523 int funcTmpl<int>(double a); 525 template <class T, class U = double> 528 double varTmpl<int> = 10.0; 531 getSymbols(FilePath), 541 AllOf(WithName(
"funcTmpl"),
Children()),
542 AllOf(WithName(
"funcTmpl<int>"),
Children()),
543 AllOf(WithName(
"varTmpl"),
Children()),
544 AllOf(WithName(
"varTmpl<int>"),
Children())));
547 TEST_F(DocumentSymbolsTest, Namespaces) {
548 std::string FilePath =
testPath(
"foo.cpp");
549 addFile(FilePath, R
"cpp( 561 inline namespace nb { 566 // This is still inlined. 573 getSymbols(FilePath), 574 ElementsAreArray<::testing::Matcher<DocumentSymbol>>( 575 {AllOf(WithName("ans1"),
577 AllOf(WithName(
"ans2"),
Children(WithName(
"ai2"))))),
578 AllOf(WithName(
"(anonymous namespace)"),
Children(WithName(
"test"))),
579 AllOf(WithName(
"na"),
581 AllOf(WithName(
"na"),
586 std::string FilePath =
testPath(
"foo.cpp");
587 addFile(FilePath, R
"( 604 getSymbols(FilePath), 606 AllOf(WithName("(anonymous enum)"),
Children(WithName(
"Red"))),
607 AllOf(WithName(
"Color"),
Children(WithName(
"Green"))),
608 AllOf(WithName(
"Color2"),
Children(WithName(
"Yellow"))),
609 AllOf(WithName(
"ns"),
Children(AllOf(WithName(
"(anonymous enum)"),
614 std::string FilePath =
testPath(
"foo.cpp");
617 class name##_Test {}; 619 $expansion[[FF]](abc); 622 class $spelling[[Test]] {}; 626 addFile(FilePath, Main.code()); 628 getSymbols(FilePath), 630 AllOf(WithName("abc_Test"), SymNameRange(Main.
range(
"expansion"))),
631 AllOf(WithName(
"Test"), SymNameRange(Main.
range(
"spelling")))));
634 TEST_F(DocumentSymbolsTest, FuncTemplates) {
635 std::string FilePath =
testPath(
"foo.cpp");
641 auto y = foo<double>() 643 addFile(FilePath, Source.code()); 645 EXPECT_THAT(getSymbols(FilePath),
646 ElementsAre(WithName(
"foo"), WithName(
"x"), WithName(
"y")));
649 TEST_F(DocumentSymbolsTest, UsingDirectives) {
650 std::string FilePath =
testPath(
"foo.cpp");
656 namespace ns_alias = ns; 658 using namespace ::ns; // check we don't loose qualifiers. 659 using namespace ns_alias; // and namespace aliases. 661 addFile(FilePath, Source.code()); 662 EXPECT_THAT(getSymbols(FilePath), 663 ElementsAre(WithName("ns"), WithName(
"ns_alias"),
664 WithName(
"using namespace ::ns"),
665 WithName(
"using namespace ns_alias")));
669 addFile(
"foo.cpp", R
"cpp( 670 template <typename T, typename U, int X = 5> class Foo {}; 671 template <typename T> class Foo<int, T> {}; 672 template <> class Foo<bool, int> {}; 673 template <> class Foo<bool, int, 3> {}; 677 getSymbols(
"foo.cpp"),
678 UnorderedElementsAre(
685 TEST_F(DocumentSymbolsTest, Qualifiers) {
686 addFile(
"foo.cpp", R
"cpp( 687 namespace foo { namespace bar { 696 struct foo::bar::Cls { }; 698 int foo::bar::func1() { return 10; } 699 int ::foo::bar::func2() { return 20; } 702 int bar::func3() { return 30; } 704 namespace alias = foo::bar; 705 int ::alias::func4() { return 40; } 709 EXPECT_THAT(getSymbols(
"foo.cpp"),
710 UnorderedElementsAre(
711 WithName(
"foo"), WithName(
"foo::bar::Cls"),
712 WithName(
"foo::bar::func1"), WithName(
"::foo::bar::func2"),
713 WithName(
"using namespace foo"), WithName(
"bar::func3"),
714 WithName(
"alias"), WithName(
"::alias::func4")));
717 TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) {
718 addFile(
"foo.cpp", R
"cpp( 719 template <typename T, typename U = double> class Foo; 722 class Foo<int, double> { 728 using int_type = int; 730 // Typedefs should be preserved! 731 int Foo<int_type, double>::method1() { return 10; } 733 // Default arguments should not be shown! 734 int Foo<int>::method2() { return 20; } 736 using Foo_type = Foo<int>; 737 // If the whole type is aliased, this should be preserved too! 738 int Foo_type::method3() { return 30; } 741 getSymbols("foo.cpp"),
742 UnorderedElementsAre(WithName(
"Foo"), WithName(
"Foo<int, double>"),
743 WithName(
"int_type"),
744 WithName(
"Foo<int_type, double>::method1"),
745 WithName(
"Foo<int>::method2"), WithName(
"Foo_type"),
746 WithName(
"Foo_type::method3")));
void addDocument(PathRef File, StringRef Contents, WantDiagnostics WD=WantDiagnostics::Auto)
Add a File to the list of tracked C++ files or update the contents if File is already tracked...
clangd::Range range(llvm::StringRef Name="") const
llvm::StringMap< std::string > Files
llvm::Expected< std::vector< DocumentSymbol > > runDocumentSymbols(ClangdServer &Server, PathRef File)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
LLVM_NODISCARD bool blockUntilIdleForTest(llvm::Optional< double > TimeoutSeconds=10)
static Options optsForTest()
MockFSProvider FSProvider
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string testPath(PathRef File)
std::string Path
A typedef to represent a file path.
virtual void onDiagnosticsReady(PathRef File, std::vector< Diag > Diagnostics)=0
Called by ClangdServer when Diagnostics for File are ready.
static constexpr llvm::StringLiteral Name
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Expected< std::vector< SymbolInformation > > runWorkspaceSymbols(ClangdServer &Server, llvm::StringRef Query, int Limit)
MockCompilationDatabase CDB
CharSourceRange Range
SourceRange for the file name.
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges...
IgnoreDiagnostics DiagConsumer
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...