| //===-- FindSymbolsTests.cpp -------------------------*- C++ -*------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "Annotations.h" |
| #include "ClangdServer.h" |
| #include "FindSymbols.h" |
| #include "SyncAPI.h" |
| #include "TestFS.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace clang { |
| namespace clangd { |
| |
| namespace { |
| |
| using ::testing::AllOf; |
| using ::testing::AnyOf; |
| using ::testing::ElementsAre; |
| using ::testing::IsEmpty; |
| using ::testing::UnorderedElementsAre; |
| |
| class IgnoreDiagnostics : public DiagnosticsConsumer { |
| void onDiagnosticsReady(PathRef File, |
| std::vector<Diag> Diagnostics) override {} |
| }; |
| |
| // GMock helpers for matching SymbolInfos items. |
| MATCHER_P(Named, Name, "") { return arg.name == Name; } |
| MATCHER_P(InContainer, ContainerName, "") { |
| return arg.containerName == ContainerName; |
| } |
| MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; } |
| |
| ClangdServer::Options optsForTests() { |
| auto ServerOpts = ClangdServer::optsForTest(); |
| ServerOpts.BuildDynamicSymbolIndex = true; |
| ServerOpts.URISchemes = {"unittest", "file"}; |
| return ServerOpts; |
| } |
| |
| class WorkspaceSymbolsTest : public ::testing::Test { |
| public: |
| WorkspaceSymbolsTest() |
| : Server(CDB, FSProvider, DiagConsumer, optsForTests()) { |
| // Make sure the test root directory is created. |
| FSProvider.Files[testPath("unused")] = ""; |
| Server.setRootPath(testRoot()); |
| } |
| |
| protected: |
| MockFSProvider FSProvider; |
| MockCompilationDatabase CDB; |
| IgnoreDiagnostics DiagConsumer; |
| ClangdServer Server; |
| int Limit = 0; |
| |
| std::vector<SymbolInformation> getSymbols(StringRef Query) { |
| EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble"; |
| auto SymbolInfos = runWorkspaceSymbols(Server, Query, Limit); |
| EXPECT_TRUE(bool(SymbolInfos)) << "workspaceSymbols returned an error"; |
| return *SymbolInfos; |
| } |
| |
| void addFile(StringRef FileName, StringRef Contents) { |
| auto Path = testPath(FileName); |
| FSProvider.Files[Path] = Contents; |
| Server.addDocument(Path, Contents); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST_F(WorkspaceSymbolsTest, NoMacro) { |
| addFile("foo.cpp", R"cpp( |
| #define MACRO X |
| )cpp"); |
| |
| // Macros are not in the index. |
| EXPECT_THAT(getSymbols("macro"), IsEmpty()); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, NoLocals) { |
| addFile("foo.cpp", R"cpp( |
| void test(int FirstParam, int SecondParam) { |
| struct LocalClass {}; |
| int local_var; |
| })cpp"); |
| EXPECT_THAT(getSymbols("l"), IsEmpty()); |
| EXPECT_THAT(getSymbols("p"), IsEmpty()); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, Globals) { |
| addFile("foo.h", R"cpp( |
| int global_var; |
| |
| int global_func(); |
| |
| struct GlobalStruct {};)cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("global"), |
| UnorderedElementsAre(AllOf(Named("GlobalStruct"), InContainer(""), |
| WithKind(SymbolKind::Struct)), |
| AllOf(Named("global_func"), InContainer(""), |
| WithKind(SymbolKind::Function)), |
| AllOf(Named("global_var"), InContainer(""), |
| WithKind(SymbolKind::Variable)))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, Unnamed) { |
| addFile("foo.h", R"cpp( |
| struct { |
| int InUnnamed; |
| } UnnamedStruct;)cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("UnnamedStruct"), |
| ElementsAre(AllOf(Named("UnnamedStruct"), |
| WithKind(SymbolKind::Variable)))); |
| EXPECT_THAT( |
| getSymbols("InUnnamed"), |
| ElementsAre(AllOf(Named("InUnnamed"), InContainer("(anonymous struct)"), |
| WithKind(SymbolKind::Field)))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, InMainFile) { |
| addFile("foo.cpp", R"cpp( |
| int test() { |
| } |
| )cpp"); |
| EXPECT_THAT(getSymbols("test"), IsEmpty()); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, Namespaces) { |
| addFile("foo.h", R"cpp( |
| namespace ans1 { |
| int ai1; |
| namespace ans2 { |
| int ai2; |
| } |
| } |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT( |
| getSymbols("a"), |
| UnorderedElementsAre(AllOf(Named("ans1"), InContainer("")), |
| AllOf(Named("ai1"), InContainer("ans1")), |
| AllOf(Named("ans2"), InContainer("ans1")), |
| AllOf(Named("ai2"), InContainer("ans1::ans2")))); |
| EXPECT_THAT(getSymbols("::"), |
| ElementsAre(AllOf(Named("ans1"), InContainer("")))); |
| EXPECT_THAT(getSymbols("::a"), |
| ElementsAre(AllOf(Named("ans1"), InContainer("")))); |
| EXPECT_THAT(getSymbols("ans1::"), |
| UnorderedElementsAre(AllOf(Named("ai1"), InContainer("ans1")), |
| AllOf(Named("ans2"), InContainer("ans1")))); |
| EXPECT_THAT(getSymbols("::ans1"), |
| ElementsAre(AllOf(Named("ans1"), InContainer("")))); |
| EXPECT_THAT(getSymbols("::ans1::"), |
| UnorderedElementsAre(AllOf(Named("ai1"), InContainer("ans1")), |
| AllOf(Named("ans2"), InContainer("ans1")))); |
| EXPECT_THAT(getSymbols("::ans1::ans2"), |
| ElementsAre(AllOf(Named("ans2"), InContainer("ans1")))); |
| EXPECT_THAT(getSymbols("::ans1::ans2::"), |
| ElementsAre(AllOf(Named("ai2"), InContainer("ans1::ans2")))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, AnonymousNamespace) { |
| addFile("foo.h", R"cpp( |
| namespace { |
| void test() {} |
| } |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("test"), IsEmpty()); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, MultiFile) { |
| addFile("foo.h", R"cpp( |
| int foo() { |
| } |
| )cpp"); |
| addFile("foo2.h", R"cpp( |
| int foo2() { |
| } |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| #include "foo2.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("foo"), |
| UnorderedElementsAre(AllOf(Named("foo"), InContainer("")), |
| AllOf(Named("foo2"), InContainer("")))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, GlobalNamespaceQueries) { |
| addFile("foo.h", R"cpp( |
| int foo() { |
| } |
| class Foo { |
| int a; |
| }; |
| namespace ns { |
| int foo2() { |
| } |
| } |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT( |
| getSymbols("::"), |
| UnorderedElementsAre( |
| AllOf(Named("Foo"), InContainer(""), WithKind(SymbolKind::Class)), |
| AllOf(Named("foo"), InContainer(""), WithKind(SymbolKind::Function)), |
| AllOf(Named("ns"), InContainer(""), |
| WithKind(SymbolKind::Namespace)))); |
| EXPECT_THAT(getSymbols(":"), IsEmpty()); |
| EXPECT_THAT(getSymbols(""), IsEmpty()); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, Enums) { |
| addFile("foo.h", R"cpp( |
| enum { |
| Red |
| }; |
| enum Color { |
| Green |
| }; |
| enum class Color2 { |
| Yellow |
| }; |
| namespace ns { |
| enum { |
| Black |
| }; |
| enum Color3 { |
| Blue |
| }; |
| enum class Color4 { |
| White |
| }; |
| } |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("Red"), ElementsAre(Named("Red"))); |
| EXPECT_THAT(getSymbols("::Red"), ElementsAre(Named("Red"))); |
| EXPECT_THAT(getSymbols("Green"), ElementsAre(Named("Green"))); |
| EXPECT_THAT(getSymbols("Green"), ElementsAre(Named("Green"))); |
| EXPECT_THAT(getSymbols("Color2::Yellow"), ElementsAre(Named("Yellow"))); |
| EXPECT_THAT(getSymbols("Yellow"), ElementsAre(Named("Yellow"))); |
| |
| EXPECT_THAT(getSymbols("ns::Black"), ElementsAre(Named("Black"))); |
| EXPECT_THAT(getSymbols("ns::Blue"), ElementsAre(Named("Blue"))); |
| EXPECT_THAT(getSymbols("ns::Color4::White"), ElementsAre(Named("White"))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, Ranking) { |
| addFile("foo.h", R"cpp( |
| namespace ns{} |
| function func(); |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| EXPECT_THAT(getSymbols("::"), ElementsAre(Named("func"), Named("ns"))); |
| } |
| |
| TEST_F(WorkspaceSymbolsTest, WithLimit) { |
| addFile("foo.h", R"cpp( |
| int foo; |
| int foo2; |
| )cpp"); |
| addFile("foo.cpp", R"cpp( |
| #include "foo.h" |
| )cpp"); |
| // Foo is higher ranked because of exact name match. |
| EXPECT_THAT(getSymbols("foo"), |
| UnorderedElementsAre(AllOf(Named("foo"), InContainer(""), |
| WithKind(SymbolKind::Variable)), |
| AllOf(Named("foo2"), InContainer(""), |
| WithKind(SymbolKind::Variable)))); |
| |
| Limit = 1; |
| EXPECT_THAT(getSymbols("foo"), ElementsAre(Named("foo"))); |
| } |
| |
| } // namespace clangd |
| } // namespace clang |