[clangd] CodeCompleteTests cleanup: naming, ordering, helpers. NFC

llvm-svn: 322824
This commit is contained in:
Sam McCall 2018-01-18 09:27:56 +00:00
parent 1f8c035d6b
commit a15c2d683e
1 changed files with 179 additions and 186 deletions

View File

@ -97,8 +97,25 @@ Matcher<const std::vector<CompletionItem> &> Has(std::string Name,
}
MATCHER(IsDocumented, "") { return !arg.documentation.empty(); }
std::unique_ptr<SymbolIndex> memIndex(std::vector<Symbol> Symbols) {
SymbolSlab::Builder Slab;
for (const auto &Sym : Symbols)
Slab.insert(Sym);
return MemIndex::build(std::move(Slab).build());
}
// Builds a server and runs code completion.
// If IndexSymbols is non-empty, an index will be built and passed to opts.
CompletionList completions(StringRef Text,
std::vector<Symbol> IndexSymbols = {},
clangd::CodeCompleteOptions Opts = {}) {
std::unique_ptr<SymbolIndex> OverrideIndex;
if (!IndexSymbols.empty()) {
assert(!Opts.Index && "both Index and IndexSymbols given!");
OverrideIndex = memIndex(std::move(IndexSymbols));
Opts.Index = OverrideIndex.get();
}
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
@ -116,6 +133,27 @@ CompletionList completions(StringRef Text,
return CompletionList;
}
// Helpers to produce fake index symbols for memIndex() or completions().
Symbol sym(StringRef QName, index::SymbolKind Kind) {
Symbol Sym;
Sym.ID = SymbolID(QName);
size_t Pos = QName.rfind("::");
if (Pos == llvm::StringRef::npos) {
Sym.Name = QName;
Sym.Scope = "";
} else {
Sym.Name = QName.substr(Pos + 2);
Sym.Scope = QName.substr(0, Pos);
}
Sym.CompletionPlainInsertText = Sym.Name;
Sym.CompletionLabel = Sym.Name;
Sym.SymInfo.Kind = Kind;
return Sym;
}
Symbol func(StringRef Name) { return sym(Name, index::SymbolKind::Function); }
Symbol cls(StringRef Name) { return sym(Name, index::SymbolKind::Class); }
Symbol var(StringRef Name) { return sym(Name, index::SymbolKind::Variable); }
TEST(CompletionTest, Limit) {
clangd::CodeCompleteOptions Opts;
Opts.Limit = 2;
@ -127,7 +165,7 @@ struct ClassWithMembers {
}
int main() { ClassWithMembers().^ }
)cpp",
Opts);
/*IndexSymbols=*/{}, Opts);
EXPECT_TRUE(Results.isIncomplete);
EXPECT_THAT(Results.items, ElementsAre(Named("AAA"), Named("BBB")));
@ -188,7 +226,7 @@ void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
ClassWithMembers().^
}
)cpp",
Opts);
/*IndexSymbols=*/{}, Opts);
// Class members. The only items that must be present in after-dot
// completion.
@ -233,7 +271,7 @@ void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
^
}
)cpp",
Opts);
/*IndexSymbols=*/{}, Opts);
// Class members. Should never be present in global completions.
EXPECT_THAT(Results.items,
@ -355,7 +393,7 @@ TEST(CompletionTest, Snippets) {
f.^
}
)cpp",
Opts);
/*IndexSymbols=*/{}, Opts);
EXPECT_THAT(Results.items,
HasSubsequence(Snippet("a"),
Snippet("f(${1:int i}, ${2:const float f})")));
@ -375,9 +413,6 @@ TEST(CompletionTest, Kinds) {
EXPECT_THAT(Results.items, Has("Struct", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Has("MACRO", CompletionItemKind::Text));
clangd::CodeCompleteOptions Opts;
Opts.EnableSnippets = true; // Needed for code patterns.
Results = completions("nam^");
EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
}
@ -406,6 +441,143 @@ TEST(CompletionTest, FuzzyRanking) {
EXPECT_THAT(Items, ElementsAre(Named("BigBang"), Named("Babble")));
}
TEST(CompletionTest, NoIndex) {
auto Results = completions(R"cpp(
namespace ns { class Local {}; }
void f() { ns::^ }
)cpp");
EXPECT_THAT(Results.items, Has("Local"));
}
TEST(CompletionTest, StaticAndDynamicIndex) {
clangd::CodeCompleteOptions Opts;
auto StaticIdx = memIndex({cls("ns::XYZ")});
auto DynamicIdx = memIndex({func("ns::foo")});
auto Merge = mergeIndex(DynamicIdx.get(), StaticIdx.get());
Opts.Index = Merge.get();
auto Results = completions(
R"cpp(
void f() { ::ns::^ }
)cpp",
/*IndexSymbols=*/{}, Opts);
EXPECT_THAT(Results.items, Contains(Labeled("[I]XYZ")));
EXPECT_THAT(Results.items, Contains(Labeled("[I]foo")));
}
TEST(CompletionTest, IndexScope) {
auto Results = completions(
R"cpp(
namespace ns { int local; }
void f() { ns::^ }
)cpp",
{cls("ns::XYZ"), cls("nx::XYZ"), func("ns::foo")});
EXPECT_THAT(Results.items,
UnorderedElementsAre(Named("XYZ"), Named("foo"), Named("local")));
}
TEST(CompletionTest, IndexBasedWithFilter) {
auto Results = completions(
R"cpp(
void f() { ns::x^ }
)cpp",
{cls("ns::XYZ"), func("ns::foo")});
EXPECT_THAT(Results.items,
UnorderedElementsAre(AllOf(Named("XYZ"), Filter("XYZ"))));
}
TEST(CompletionTest, IndexGlobalQualified) {
auto Results = completions(
R"cpp(
void f() { ::^ }
)cpp",
{cls("XYZ")});
EXPECT_THAT(Results.items, AllOf(Has("XYZ", CompletionItemKind::Class),
Has("f", CompletionItemKind::Function)));
}
TEST(CompletionTest, IndexFullyQualifiedScope) {
auto Results = completions(
R"cpp(
void f() { ::ns::^ }
)cpp",
{cls("ns::XYZ")});
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
}
TEST(CompletionTest, IndexSuppressesPreambleCompletions) {
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true);
FS.Files[getVirtualTestFilePath("bar.h")] =
R"cpp(namespace ns { int preamble; })cpp";
auto File = getVirtualTestFilePath("foo.cpp");
Annotations Test(R"cpp(
#include "bar.h"
namespace ns { int local; }
void f() { ns::^ }
)cpp");
Server.addDocument(Context::empty(), File, Test.code()).wait();
clangd::CodeCompleteOptions Opts = {};
auto WithoutIndex =
Server.codeComplete(Context::empty(), File, Test.point(), Opts)
.get()
.second.Value;
EXPECT_THAT(WithoutIndex.items,
UnorderedElementsAre(Named("local"), Named("preamble")));
auto I = memIndex({var("ns::index")});
Opts.Index = I.get();
auto WithIndex =
Server.codeComplete(Context::empty(), File, Test.point(), Opts)
.get()
.second.Value;
EXPECT_THAT(WithIndex.items,
UnorderedElementsAre(Named("local"), Named("index")));
}
TEST(CompletionTest, DynamicIndexMultiFile) {
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true,
/*BuildDynamicSymbolIndex=*/true);
Server
.addDocument(Context::empty(), getVirtualTestFilePath("foo.cpp"), R"cpp(
namespace ns { class XYZ {}; void foo(int x) {} }
)cpp")
.wait();
auto File = getVirtualTestFilePath("bar.cpp");
Annotations Test(R"cpp(
namespace ns {
class XXX {};
/// Doooc
void fooooo() {}
}
void f() { ns::^ }
)cpp");
Server.addDocument(Context::empty(), File, Test.code()).wait();
auto Results = Server.codeComplete(Context::empty(), File, Test.point(), {})
.get()
.second.Value;
// "XYZ" and "foo" are not included in the file being completed but are still
// visible through the index.
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function));
EXPECT_THAT(Results.items, Has("XXX", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Contains(AllOf(Named("fooooo"), Filter("fooooo"),
Kind(CompletionItemKind::Function),
Doc("Doooc"), Detail("void"))));
}
SignatureHelp signatures(StringRef Text) {
MockFSProvider FS;
MockCompilationDatabase CDB;
@ -481,185 +653,6 @@ TEST(SignatureHelpTest, ActiveArg) {
EXPECT_EQ(1, Results.activeParameter);
}
std::unique_ptr<SymbolIndex> simpleIndexFromSymbols(
std::vector<std::pair<std::string, index::SymbolKind>> Symbols) {
SymbolSlab::Builder Slab;
for (const auto &Pair : Symbols) {
Symbol Sym;
Sym.ID = SymbolID(Pair.first);
llvm::StringRef QName = Pair.first;
size_t Pos = QName.rfind("::");
if (Pos == llvm::StringRef::npos) {
Sym.Name = QName;
Sym.Scope = "";
} else {
Sym.Name = QName.substr(Pos + 2);
Sym.Scope = QName.substr(0, Pos);
}
Sym.CompletionPlainInsertText = Sym.Name;
Sym.SymInfo.Kind = Pair.second;
Slab.insert(Sym);
}
return MemIndex::build(std::move(Slab).build());
}
TEST(CompletionTest, NoIndex) {
clangd::CodeCompleteOptions Opts;
Opts.Index = nullptr;
auto Results = completions(R"cpp(
namespace ns { class Local {}; }
void f() { ns::^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Has("Local"));
}
TEST(CompletionTest, StaticAndDynamicIndex) {
clangd::CodeCompleteOptions Opts;
auto StaticIdx =
simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
auto DynamicIdx =
simpleIndexFromSymbols({{"ns::foo", index::SymbolKind::Function}});
auto Merge = mergeIndex(DynamicIdx.get(), StaticIdx.get());
Opts.Index = Merge.get();
auto Results = completions(R"cpp(
void f() { ::ns::^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Contains(Labeled("[I]XYZ")));
EXPECT_THAT(Results.items, Contains(Labeled("[I]foo")));
}
TEST(CompletionTest, SimpleIndexBased) {
clangd::CodeCompleteOptions Opts;
auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
{"nx::XYZ", index::SymbolKind::Class},
{"ns::foo", index::SymbolKind::Function}});
Opts.Index = I.get();
auto Results = completions(R"cpp(
namespace ns { int local; }
void f() { ns::^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function));
EXPECT_THAT(Results.items, Has("local"));
}
TEST(CompletionTest, IndexBasedWithFilter) {
clangd::CodeCompleteOptions Opts;
auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
{"ns::foo", index::SymbolKind::Function}});
Opts.Index = I.get();
auto Results = completions(R"cpp(
void f() { ns::x^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Contains(AllOf(Named("XYZ"), Filter("XYZ"))));
EXPECT_THAT(Results.items, Not(Has("foo")));
}
TEST(CompletionTest, GlobalQualified) {
clangd::CodeCompleteOptions Opts;
auto I = simpleIndexFromSymbols({{"XYZ", index::SymbolKind::Class}});
Opts.Index = I.get();
auto Results = completions(R"cpp(
void f() { ::^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
}
TEST(CompletionTest, FullyQualifiedScope) {
clangd::CodeCompleteOptions Opts;
auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
Opts.Index = I.get();
auto Results = completions(R"cpp(
void f() { ::ns::^ }
)cpp",
Opts);
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
}
TEST(CompletionTest, IndexSuppressesPreambleCompletions) {
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true);
FS.Files[getVirtualTestFilePath("bar.h")] =
R"cpp(namespace ns { int preamble; })cpp";
auto File = getVirtualTestFilePath("foo.cpp");
Annotations Test(R"cpp(
#include "bar.h"
namespace ns { int local; }
void f() { ns::^ }
)cpp");
Server.addDocument(Context::empty(), File, Test.code()).wait();
clangd::CodeCompleteOptions Opts = {};
auto WithoutIndex =
Server.codeComplete(Context::empty(), File, Test.point(), Opts)
.get()
.second.Value;
EXPECT_THAT(WithoutIndex.items,
UnorderedElementsAre(Named("local"), Named("preamble")));
auto I = simpleIndexFromSymbols({{"ns::index", index::SymbolKind::Variable}});
Opts.Index = I.get();
auto WithIndex =
Server.codeComplete(Context::empty(), File, Test.point(), Opts)
.get()
.second.Value;
EXPECT_THAT(WithIndex.items,
UnorderedElementsAre(Named("local"), Named("index")));
}
TEST(CompletionTest, ASTIndexMultiFile) {
MockFSProvider FS;
MockCompilationDatabase CDB;
IgnoreDiagnostics DiagConsumer;
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true,
/*BuildDynamicSymbolIndex=*/true);
Server
.addDocument(Context::empty(), getVirtualTestFilePath("foo.cpp"), R"cpp(
namespace ns { class XYZ {}; void foo(int x) {} }
)cpp")
.wait();
auto File = getVirtualTestFilePath("bar.cpp");
Annotations Test(R"cpp(
namespace ns {
class XXX {};
/// Doooc
void fooooo() {}
}
void f() { ns::^ }
)cpp");
Server.addDocument(Context::empty(), File, Test.code()).wait();
auto Results = Server.codeComplete(Context::empty(), File, Test.point(), {})
.get()
.second.Value;
// "XYZ" and "foo" are not included in the file being completed but are still
// visible through the index.
EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function));
EXPECT_THAT(Results.items, Has("XXX", CompletionItemKind::Class));
EXPECT_THAT(Results.items, Contains(AllOf(Named("fooooo"), Filter("fooooo"),
Kind(CompletionItemKind::Function),
Doc("Doooc"), Detail("void"))));
}
} // namespace
} // namespace clangd
} // namespace clang