summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp')
-rw-r--r--clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp176
1 files changed, 100 insertions, 76 deletions
diff --git a/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp b/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
index b235549724f..7eb7b137248 100644
--- a/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
+++ b/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
@@ -17,7 +17,6 @@
#include "SourceCode.h"
#include "TestFS.h"
#include "index/MemIndex.h"
-#include "index/Merge.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -82,7 +81,7 @@ MATCHER_P(Snippet, Text, "") {
return arg.insertTextFormat == clangd::InsertTextFormat::Snippet &&
arg.insertText == Text;
}
-MATCHER(FilterContainsName, "") {
+MATCHER(NameContainsFilter, "") {
if (arg.filterText.empty())
return true;
return llvm::StringRef(arg.insertText).contains(arg.filterText);
@@ -129,14 +128,27 @@ CompletionList completions(StringRef Text,
.get()
.second.Value;
// Sanity-check that filterText is valid.
- EXPECT_THAT(CompletionList.items, Each(FilterContainsName()));
+ EXPECT_THAT(CompletionList.items, Each(NameContainsFilter()));
return CompletionList;
}
+std::string replace(StringRef Haystack, StringRef Needle, StringRef Repl) {
+ std::string Result;
+ raw_string_ostream OS(Result);
+ std::pair<StringRef, StringRef> Split;
+ for (Split = Haystack.split(Needle); !Split.second.empty();
+ Split = Split.first.split(Needle))
+ OS << Split.first << Repl;
+ Result += Split.first;
+ OS.flush();
+ return Result;
+}
+
// Helpers to produce fake index symbols for memIndex() or completions().
-Symbol sym(StringRef QName, index::SymbolKind Kind) {
+// USRFormat is a regex replacement string for the unqualified part of the USR.
+Symbol sym(StringRef QName, index::SymbolKind Kind, StringRef USRFormat) {
Symbol Sym;
- Sym.ID = SymbolID(QName);
+ std::string USR = "c:"; // We synthesize a few simple cases of USRs by hand!
size_t Pos = QName.rfind("::");
if (Pos == llvm::StringRef::npos) {
Sym.Name = QName;
@@ -144,15 +156,25 @@ Symbol sym(StringRef QName, index::SymbolKind Kind) {
} else {
Sym.Name = QName.substr(Pos + 2);
Sym.Scope = QName.substr(0, Pos);
+ USR += "@N@" + replace(Sym.Scope, "::", "@N@"); // ns:: -> @N@ns
}
+ USR += Regex("^.*$").sub(USRFormat, Sym.Name); // e.g. func -> @F@func#
+ Sym.ID = SymbolID(USR);
Sym.CompletionPlainInsertText = Sym.Name;
+ Sym.CompletionSnippetInsertText = 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); }
+Symbol func(StringRef Name) { // Assumes the function has no args.
+ return sym(Name, index::SymbolKind::Function, "@F@\\0#"); // no args
+}
+Symbol cls(StringRef Name) {
+ return sym(Name, index::SymbolKind::Class, "@S@\\0@S@\\0");
+}
+Symbol var(StringRef Name) {
+ return sym(Name, index::SymbolKind::Variable, "@\\0");
+}
TEST(CompletionTest, Limit) {
clangd::CodeCompleteOptions Opts;
@@ -226,7 +248,7 @@ void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
ClassWithMembers().^
}
)cpp",
- /*IndexSymbols=*/{}, Opts);
+ {cls("IndexClass"), var("index_var"), func("index_func")}, Opts);
// Class members. The only items that must be present in after-dot
// completion.
@@ -236,9 +258,11 @@ void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
EXPECT_IFF(Opts.IncludeIneligibleResults, Results.items,
Has("private_field"));
// Global items.
- EXPECT_THAT(Results.items, Not(AnyOf(Has("global_var"), Has("global_func"),
- Has("global_func()"), Has("GlobalClass"),
- Has("MACRO"), Has("LocalClass"))));
+ EXPECT_THAT(
+ Results.items,
+ Not(AnyOf(Has("global_var"), Has("index_var"), Has("global_func"),
+ Has("global_func()"), Has("index_func"), Has("GlobalClass"),
+ Has("IndexClass"), Has("MACRO"), Has("LocalClass"))));
// There should be no code patterns (aka snippets) in after-dot
// completion. At least there aren't any we're aware of.
EXPECT_THAT(Results.items, Not(Contains(Kind(CompletionItemKind::Snippet))));
@@ -271,16 +295,17 @@ void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
^
}
)cpp",
- /*IndexSymbols=*/{}, Opts);
+ {cls("IndexClass"), var("index_var"), func("index_func")}, Opts);
// Class members. Should never be present in global completions.
EXPECT_THAT(Results.items,
Not(AnyOf(Has("method"), Has("method()"), Has("field"))));
// Global items.
EXPECT_THAT(Results.items,
- AllOf(Has("global_var"),
+ AllOf(Has("global_var"), Has("index_var"),
Has(Opts.EnableSnippets ? "global_func()" : "global_func"),
- Has("GlobalClass")));
+ Has("index_func" /* our fake symbol doesn't include () */),
+ Has("GlobalClass"), Has("IndexClass")));
// A macro.
EXPECT_IFF(Opts.IncludeMacros, Results.items, Has("MACRO"));
// Local items. Must be present always.
@@ -399,83 +424,67 @@ TEST(CompletionTest, Snippets) {
}
TEST(CompletionTest, Kinds) {
- auto Results = completions(R"cpp(
- #define MACRO X
- int variable;
- struct Struct {};
- int function();
- int X = ^
- )cpp");
- EXPECT_THAT(Results.items, Has("function", CompletionItemKind::Function));
- EXPECT_THAT(Results.items, Has("variable", CompletionItemKind::Variable));
- EXPECT_THAT(Results.items, Has("int", CompletionItemKind::Keyword));
- EXPECT_THAT(Results.items, Has("Struct", CompletionItemKind::Class));
- EXPECT_THAT(Results.items, Has("MACRO", CompletionItemKind::Text));
+ auto Results = completions(
+ R"cpp(
+ #define MACRO X
+ int variable;
+ struct Struct {};
+ int function();
+ int X = ^
+ )cpp",
+ {func("indexFunction"), var("indexVariable"), cls("indexClass")});
+ EXPECT_THAT(Results.items,
+ AllOf(Has("function", CompletionItemKind::Function),
+ Has("variable", CompletionItemKind::Variable),
+ Has("int", CompletionItemKind::Keyword),
+ Has("Struct", CompletionItemKind::Class),
+ Has("MACRO", CompletionItemKind::Text),
+ Has("indexFunction", CompletionItemKind::Function),
+ Has("indexVariable", CompletionItemKind::Variable),
+ Has("indexClass", CompletionItemKind::Class)));
Results = completions("nam^");
EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
}
TEST(CompletionTest, NoDuplicates) {
- auto Items = completions(R"cpp(
-struct Adapter {
- void method();
-};
+ auto Results = completions(
+ R"cpp(
+ class Adapter {
+ void method();
+ };
-void Adapter::method() {
- Adapter^
-}
- )cpp")
- .items;
+ void Adapter::method() {
+ Adapter^
+ }
+ )cpp",
+ {cls("Adapter")});
// Make sure there are no duplicate entries of 'Adapter'.
- EXPECT_THAT(Items, ElementsAre(Named("Adapter"), Named("~Adapter")));
-}
-
-TEST(CompletionTest, FuzzyRanking) {
- auto Items = completions(R"cpp(
- struct fake { int BigBang, Babble, Ball; };
- int main() { fake().bb^ }")cpp").items;
- // BigBang is a better match than Babble. Ball doesn't match at all.
- 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"));
+ EXPECT_THAT(Results.items, ElementsAre(Named("Adapter"), Named("~Adapter")));
}
-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();
-
+TEST(CompletionTest, ScopedNoIndex) {
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")));
+ namespace fake { int BigBang, Babble, Ball; };
+ int main() { fake::bb^ }
+ ")cpp");
+ // BigBang is a better match than Babble. Ball doesn't match at all.
+ EXPECT_THAT(Results.items, ElementsAre(Named("BigBang"), Named("Babble")));
}
-TEST(CompletionTest, IndexScope) {
+TEST(CompletionTest, Scoped) {
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")));
+ namespace fake { int Babble, Ball; };
+ int main() { fake::bb^ }
+ ")cpp",
+ {var("fake::BigBang")});
+ EXPECT_THAT(Results.items, ElementsAre(Named("BigBang"), Named("Babble")));
}
-TEST(CompletionTest, IndexBasedWithFilter) {
+TEST(CompletionTest, ScopedWithFilter) {
auto Results = completions(
R"cpp(
void f() { ns::x^ }
@@ -485,7 +494,7 @@ TEST(CompletionTest, IndexBasedWithFilter) {
UnorderedElementsAre(AllOf(Named("XYZ"), Filter("XYZ"))));
}
-TEST(CompletionTest, IndexGlobalQualified) {
+TEST(CompletionTest, GlobalQualified) {
auto Results = completions(
R"cpp(
void f() { ::^ }
@@ -495,13 +504,28 @@ TEST(CompletionTest, IndexGlobalQualified) {
Has("f", CompletionItemKind::Function)));
}
-TEST(CompletionTest, IndexFullyQualifiedScope) {
+TEST(CompletionTest, FullyQualified) {
auto Results = completions(
R"cpp(
+ namespace ns { void bar(); }
void f() { ::ns::^ }
)cpp",
{cls("ns::XYZ")});
- EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
+ EXPECT_THAT(Results.items, AllOf(Has("XYZ", CompletionItemKind::Class),
+ Has("bar", CompletionItemKind::Function)));
+}
+
+TEST(CompletionTest, SemaIndexMerge) {
+ auto Results = completions(
+ R"cpp(
+ namespace ns { int local; void both(); }
+ void f() { ::ns::^ }
+ )cpp",
+ {func("ns::both"), cls("ns::Index")});
+ // We get results from both index and sema, with no duplicates.
+ EXPECT_THAT(
+ Results.items,
+ UnorderedElementsAre(Named("local"), Named("Index"), Named("both")));
}
TEST(CompletionTest, IndexSuppressesPreambleCompletions) {
OpenPOWER on IntegriCloud