blob: 5c55acd1376456bd36eb536e4e1f882d966c19a5 [file] [log] [blame]
Sam McCall9aad25f2017-12-05 07:20:26 +00001//===-- CodeCompleteTests.cpp -----------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "ClangdServer.h"
10#include "Compiler.h"
Sam McCall44fdcec22017-12-08 15:00:59 +000011#include "Matchers.h"
Sam McCall9aad25f2017-12-05 07:20:26 +000012#include "Protocol.h"
13#include "TestFS.h"
Sam McCallf6ae3232017-12-05 20:11:29 +000014#include "gmock/gmock.h"
Sam McCall9aad25f2017-12-05 07:20:26 +000015#include "gtest/gtest.h"
16
17namespace clang {
18namespace clangd {
Sam McCallf6ae3232017-12-05 20:11:29 +000019// Let GMock print completion items.
20void PrintTo(const CompletionItem &I, std::ostream *O) {
21 llvm::raw_os_ostream OS(*O);
Sam McCall44fdcec22017-12-08 15:00:59 +000022 OS << I.label << " - " << toJSON(I);
23}
24void PrintTo(const std::vector<CompletionItem> &V, std::ostream *O) {
25 *O << "{\n";
26 for (const auto &I : V) {
27 *O << "\t";
28 PrintTo(I, O);
29 *O << "\n";
30 }
31 *O << "}";
Sam McCallf6ae3232017-12-05 20:11:29 +000032}
33
Sam McCall9aad25f2017-12-05 07:20:26 +000034namespace {
35using namespace llvm;
Sam McCallf6ae3232017-12-05 20:11:29 +000036using ::testing::AllOf;
37using ::testing::Contains;
38using ::testing::ElementsAre;
Sam McCallf6ae3232017-12-05 20:11:29 +000039using ::testing::Not;
Sam McCall9aad25f2017-12-05 07:20:26 +000040
41class IgnoreDiagnostics : public DiagnosticsConsumer {
42 void onDiagnosticsReady(
43 PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {}
44};
45
46struct StringWithPos {
47 std::string Text;
48 clangd::Position MarkerPos;
49};
50
Sam McCallf6ae3232017-12-05 20:11:29 +000051/// Accepts a source file with a cursor marker ^.
52/// Returns the source file with the marker removed, and the marker position.
53StringWithPos parseTextMarker(StringRef Text) {
54 std::size_t MarkerOffset = Text.find('^');
55 assert(MarkerOffset != StringRef::npos && "^ wasn't found in Text.");
Sam McCall9aad25f2017-12-05 07:20:26 +000056
57 std::string WithoutMarker;
58 WithoutMarker += Text.take_front(MarkerOffset);
Sam McCallf6ae3232017-12-05 20:11:29 +000059 WithoutMarker += Text.drop_front(MarkerOffset + 1);
60 assert(StringRef(WithoutMarker).find('^') == StringRef::npos &&
61 "There were multiple occurences of ^ inside Text");
Sam McCall9aad25f2017-12-05 07:20:26 +000062
Sam McCallf6ae3232017-12-05 20:11:29 +000063 auto MarkerPos = offsetToPosition(WithoutMarker, MarkerOffset);
Sam McCall9aad25f2017-12-05 07:20:26 +000064 return {std::move(WithoutMarker), MarkerPos};
65}
66
Sam McCallf6ae3232017-12-05 20:11:29 +000067// GMock helpers for matching completion items.
68MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
Sam McCall44fdcec22017-12-08 15:00:59 +000069MATCHER_P(Labeled, Label, "") { return arg.label == Label; }
70MATCHER_P(Kind, K, "") { return arg.kind == K; }
71MATCHER_P(PlainText, Text, "") {
72 return arg.insertTextFormat == clangd::InsertTextFormat::PlainText &&
73 arg.insertText == Text;
74}
75MATCHER_P(Snippet, Text, "") {
76 return arg.insertTextFormat == clangd::InsertTextFormat::Snippet &&
77 arg.insertText == Text;
78}
Sam McCallf6ae3232017-12-05 20:11:29 +000079// Shorthand for Contains(Named(Name)).
80Matcher<const std::vector<CompletionItem> &> Has(std::string Name) {
81 return Contains(Named(std::move(Name)));
82}
Sam McCall44fdcec22017-12-08 15:00:59 +000083Matcher<const std::vector<CompletionItem> &> Has(std::string Name,
84 CompletionItemKind K) {
85 return Contains(AllOf(Named(std::move(Name)), Kind(K)));
Sam McCallf6ae3232017-12-05 20:11:29 +000086}
Sam McCall44fdcec22017-12-08 15:00:59 +000087MATCHER(IsDocumented, "") { return !arg.documentation.empty(); }
Sam McCall9aad25f2017-12-05 07:20:26 +000088
Sam McCallf6ae3232017-12-05 20:11:29 +000089CompletionList completions(StringRef Text,
90 clangd::CodeCompleteOptions Opts = {}) {
Sam McCall9aad25f2017-12-05 07:20:26 +000091 MockFSProvider FS;
Sam McCall93cd9912017-12-05 07:34:35 +000092 MockCompilationDatabase CDB;
Sam McCallf6ae3232017-12-05 20:11:29 +000093 IgnoreDiagnostics DiagConsumer;
Sam McCall9aad25f2017-12-05 07:20:26 +000094 ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
95 /*StorePreamblesInMemory=*/true,
Sam McCall9aad25f2017-12-05 07:20:26 +000096 EmptyLogger::getInstance());
Sam McCallf6ae3232017-12-05 20:11:29 +000097 auto File = getVirtualTestFilePath("foo.cpp");
98 auto Test = parseTextMarker(Text);
99 Server.addDocument(File, Test.Text);
100 return Server.codeComplete(File, Test.MarkerPos, Opts).get().Value;
Sam McCall9aad25f2017-12-05 07:20:26 +0000101}
102
Sam McCallf6ae3232017-12-05 20:11:29 +0000103TEST(CompletionTest, Limit) {
104 clangd::CodeCompleteOptions Opts;
105 Opts.Limit = 2;
106 auto Results = completions(R"cpp(
Sam McCall9aad25f2017-12-05 07:20:26 +0000107struct ClassWithMembers {
108 int AAA();
109 int BBB();
110 int CCC();
111}
Sam McCallf6ae3232017-12-05 20:11:29 +0000112int main() { ClassWithMembers().^ }
Sam McCall9aad25f2017-12-05 07:20:26 +0000113 )cpp",
Sam McCallf6ae3232017-12-05 20:11:29 +0000114 Opts);
Sam McCall9aad25f2017-12-05 07:20:26 +0000115
116 EXPECT_TRUE(Results.isIncomplete);
Sam McCallf6ae3232017-12-05 20:11:29 +0000117 EXPECT_THAT(Results.items, ElementsAre(Named("AAA"), Named("BBB")));
Sam McCall9aad25f2017-12-05 07:20:26 +0000118}
119
Sam McCallf6ae3232017-12-05 20:11:29 +0000120TEST(CompletionTest, Filter) {
121 std::string Body = R"cpp(
Sam McCall9aad25f2017-12-05 07:20:26 +0000122 int Abracadabra;
123 int Alakazam;
124 struct S {
125 int FooBar;
126 int FooBaz;
127 int Qux;
128 };
129 )cpp";
Sam McCall9aad25f2017-12-05 07:20:26 +0000130
Sam McCallf6ae3232017-12-05 20:11:29 +0000131 EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").items,
132 AllOf(Has("FooBar"), Has("FooBaz"), Not(Has("Qux"))));
Sam McCall9aad25f2017-12-05 07:20:26 +0000133
Sam McCallf6ae3232017-12-05 20:11:29 +0000134 EXPECT_THAT(completions(Body + "int main() { S().FR^ }").items,
135 AllOf(Has("FooBar"), Not(Has("FooBaz")), Not(Has("Qux"))));
Sam McCall9aad25f2017-12-05 07:20:26 +0000136
Sam McCallf6ae3232017-12-05 20:11:29 +0000137 EXPECT_THAT(completions(Body + "int main() { S().opr^ }").items,
138 Has("operator="));
Sam McCall9aad25f2017-12-05 07:20:26 +0000139
Sam McCallf6ae3232017-12-05 20:11:29 +0000140 EXPECT_THAT(completions(Body + "int main() { aaa^ }").items,
141 AllOf(Has("Abracadabra"), Has("Alakazam")));
Sam McCall9aad25f2017-12-05 07:20:26 +0000142
Sam McCallf6ae3232017-12-05 20:11:29 +0000143 EXPECT_THAT(completions(Body + "int main() { _a^ }").items,
144 AllOf(Has("static_cast"), Not(Has("Abracadabra"))));
Sam McCall9aad25f2017-12-05 07:20:26 +0000145}
146
Sam McCallf6ae3232017-12-05 20:11:29 +0000147void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
Sam McCall44fdcec22017-12-08 15:00:59 +0000148 auto Results = completions(
149 R"cpp(
150 #define MACRO X
Sam McCall9aad25f2017-12-05 07:20:26 +0000151
Sam McCall44fdcec22017-12-08 15:00:59 +0000152 int global_var;
Sam McCall9aad25f2017-12-05 07:20:26 +0000153
Sam McCall44fdcec22017-12-08 15:00:59 +0000154 int global_func();
Sam McCall9aad25f2017-12-05 07:20:26 +0000155
Sam McCall44fdcec22017-12-08 15:00:59 +0000156 struct GlobalClass {};
Sam McCall9aad25f2017-12-05 07:20:26 +0000157
Sam McCall44fdcec22017-12-08 15:00:59 +0000158 struct ClassWithMembers {
159 /// Doc for method.
160 int method();
Sam McCall9aad25f2017-12-05 07:20:26 +0000161
Sam McCall44fdcec22017-12-08 15:00:59 +0000162 int field;
163 private:
164 int private_field;
165 };
Sam McCall9aad25f2017-12-05 07:20:26 +0000166
Sam McCall44fdcec22017-12-08 15:00:59 +0000167 int test() {
168 struct LocalClass {};
Sam McCall9aad25f2017-12-05 07:20:26 +0000169
Sam McCall44fdcec22017-12-08 15:00:59 +0000170 /// Doc for local_var.
171 int local_var;
Sam McCall9aad25f2017-12-05 07:20:26 +0000172
Sam McCall44fdcec22017-12-08 15:00:59 +0000173 ClassWithMembers().^
174 }
175 )cpp",
176 Opts);
Sam McCall9aad25f2017-12-05 07:20:26 +0000177
Sam McCallf6ae3232017-12-05 20:11:29 +0000178 // Class members. The only items that must be present in after-dot
179 // completion.
Sam McCall44fdcec22017-12-08 15:00:59 +0000180 EXPECT_THAT(
181 Results.items,
182 AllOf(Has(Opts.EnableSnippets ? "method()" : "method"), Has("field")));
183 EXPECT_IFF(Opts.IncludeIneligibleResults, Results.items,
184 Has("private_field"));
Sam McCallf6ae3232017-12-05 20:11:29 +0000185 // Global items.
Sam McCall44fdcec22017-12-08 15:00:59 +0000186 EXPECT_THAT(Results.items, Not(AnyOf(Has("global_var"), Has("global_func"),
187 Has("global_func()"), Has("GlobalClass"),
188 Has("MACRO"), Has("LocalClass"))));
Sam McCallf6ae3232017-12-05 20:11:29 +0000189 // There should be no code patterns (aka snippets) in after-dot
190 // completion. At least there aren't any we're aware of.
Sam McCall44fdcec22017-12-08 15:00:59 +0000191 EXPECT_THAT(Results.items, Not(Contains(Kind(CompletionItemKind::Snippet))));
Sam McCallf6ae3232017-12-05 20:11:29 +0000192 // Check documentation.
Sam McCall44fdcec22017-12-08 15:00:59 +0000193 EXPECT_IFF(Opts.IncludeBriefComments, Results.items,
194 Contains(IsDocumented()));
Sam McCallf6ae3232017-12-05 20:11:29 +0000195}
Sam McCall9aad25f2017-12-05 07:20:26 +0000196
Sam McCallf6ae3232017-12-05 20:11:29 +0000197void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
Sam McCall44fdcec22017-12-08 15:00:59 +0000198 auto Results = completions(
199 R"cpp(
200 #define MACRO X
Sam McCall9aad25f2017-12-05 07:20:26 +0000201
Sam McCall44fdcec22017-12-08 15:00:59 +0000202 int global_var;
203 int global_func();
Sam McCall9aad25f2017-12-05 07:20:26 +0000204
Sam McCall44fdcec22017-12-08 15:00:59 +0000205 struct GlobalClass {};
Sam McCall9aad25f2017-12-05 07:20:26 +0000206
Sam McCall44fdcec22017-12-08 15:00:59 +0000207 struct ClassWithMembers {
208 /// Doc for method.
209 int method();
210 };
Sam McCall9aad25f2017-12-05 07:20:26 +0000211
Sam McCall44fdcec22017-12-08 15:00:59 +0000212 int test() {
213 struct LocalClass {};
Sam McCall9aad25f2017-12-05 07:20:26 +0000214
Sam McCall44fdcec22017-12-08 15:00:59 +0000215 /// Doc for local_var.
216 int local_var;
Sam McCall9aad25f2017-12-05 07:20:26 +0000217
Sam McCall44fdcec22017-12-08 15:00:59 +0000218 ^
219 }
220 )cpp",
221 Opts);
Sam McCallf6ae3232017-12-05 20:11:29 +0000222
223 // Class members. Should never be present in global completions.
Sam McCall44fdcec22017-12-08 15:00:59 +0000224 EXPECT_THAT(Results.items,
Sam McCallf6ae3232017-12-05 20:11:29 +0000225 Not(AnyOf(Has("method"), Has("method()"), Has("field"))));
226 // Global items.
Sam McCall44fdcec22017-12-08 15:00:59 +0000227 EXPECT_IFF(Opts.IncludeGlobals, Results.items,
Sam McCallf6ae3232017-12-05 20:11:29 +0000228 AllOf(Has("global_var"),
229 Has(Opts.EnableSnippets ? "global_func()" : "global_func"),
230 Has("GlobalClass")));
231 // A macro.
Sam McCall44fdcec22017-12-08 15:00:59 +0000232 EXPECT_IFF(Opts.IncludeMacros, Results.items, Has("MACRO"));
Sam McCallf6ae3232017-12-05 20:11:29 +0000233 // Local items. Must be present always.
Sam McCall44fdcec22017-12-08 15:00:59 +0000234 EXPECT_THAT(Results.items, AllOf(Has("local_var"), Has("LocalClass"),
235 Contains(Kind(CompletionItemKind::Snippet))));
Sam McCallf6ae3232017-12-05 20:11:29 +0000236 // Check documentation.
Sam McCall44fdcec22017-12-08 15:00:59 +0000237 EXPECT_IFF(Opts.IncludeBriefComments, Results.items,
238 Contains(IsDocumented()));
Sam McCallf6ae3232017-12-05 20:11:29 +0000239}
240
241TEST(CompletionTest, CompletionOptions) {
242 clangd::CodeCompleteOptions Opts;
Sam McCall9aad25f2017-12-05 07:20:26 +0000243 for (bool IncludeMacros : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000244 Opts.IncludeMacros = IncludeMacros;
Sam McCall9aad25f2017-12-05 07:20:26 +0000245 for (bool IncludeGlobals : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000246 Opts.IncludeGlobals = IncludeGlobals;
Sam McCall9aad25f2017-12-05 07:20:26 +0000247 for (bool IncludeBriefComments : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000248 Opts.IncludeBriefComments = IncludeBriefComments;
Sam McCall9aad25f2017-12-05 07:20:26 +0000249 for (bool EnableSnippets : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000250 Opts.EnableSnippets = EnableSnippets;
Sam McCall9aad25f2017-12-05 07:20:26 +0000251 for (bool IncludeCodePatterns : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000252 Opts.IncludeCodePatterns = IncludeCodePatterns;
Sam McCall9aad25f2017-12-05 07:20:26 +0000253 for (bool IncludeIneligibleResults : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000254 Opts.IncludeIneligibleResults = IncludeIneligibleResults;
255 TestAfterDotCompletion(Opts);
256 TestGlobalScopeCompletion(Opts);
Sam McCall9aad25f2017-12-05 07:20:26 +0000257 }
258 }
259 }
260 }
261 }
262 }
263}
264
Sam McCallf6ae3232017-12-05 20:11:29 +0000265// Check code completion works when the file contents are overridden.
266TEST(CompletionTest, CheckContentsOverride) {
267 MockFSProvider FS;
268 IgnoreDiagnostics DiagConsumer;
269 MockCompilationDatabase CDB;
270 ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
271 /*StorePreamblesInMemory=*/true,
272 EmptyLogger::getInstance());
273 auto File = getVirtualTestFilePath("foo.cpp");
274 Server.addDocument(File, "ignored text!");
275
276 auto Example = parseTextMarker("int cbc; int b = ^;");
277 auto Results =
278 Server
279 .codeComplete(File, Example.MarkerPos, clangd::CodeCompleteOptions(),
280 StringRef(Example.Text))
281 .get()
282 .Value;
283 EXPECT_THAT(Results.items, Contains(Named("cbc")));
284}
285
Sam McCall44fdcec22017-12-08 15:00:59 +0000286TEST(CompletionTest, Priorities) {
287 auto Internal = completions(R"cpp(
288 class Foo {
289 public: void pub();
290 protected: void prot();
291 private: void priv();
292 };
293 void Foo::pub() { this->^ }
294 )cpp");
295 EXPECT_THAT(Internal.items,
296 HasSubsequence(Named("priv"), Named("prot"), Named("pub")));
297
298 auto External = completions(R"cpp(
299 class Foo {
300 public: void pub();
301 protected: void prot();
302 private: void priv();
303 };
304 void test() {
305 Foo F;
306 F.^
307 }
308 )cpp");
309 EXPECT_THAT(External.items,
310 AllOf(Has("pub"), Not(Has("prot")), Not(Has("priv"))));
311}
312
313TEST(CompletionTest, Qualifiers) {
314 auto Results = completions(R"cpp(
315 class Foo {
316 public: int foo() const;
317 int bar() const;
318 };
319 class Bar : public Foo {
320 int foo() const;
321 };
322 void test() { Bar().^ }
323 )cpp");
324 EXPECT_THAT(Results.items, HasSubsequence(Labeled("bar() const"),
325 Labeled("Foo::foo() const")));
326 EXPECT_THAT(Results.items, Not(Contains(Labeled("foo() const")))); // private
327}
328
329TEST(CompletionTest, Snippets) {
330 clangd::CodeCompleteOptions Opts;
331 Opts.EnableSnippets = true;
332 auto Results = completions(
333 R"cpp(
334 struct fake {
335 int a;
336 int f(int i, const float f) const;
337 };
338 int main() {
339 fake f;
340 f.^
341 }
342 )cpp",
343 Opts);
344 EXPECT_THAT(Results.items,
345 HasSubsequence(PlainText("a"),
346 Snippet("f(${1:int i}, ${2:const float f})")));
347}
348
349TEST(CompletionTest, Kinds) {
350 auto Results = completions(R"cpp(
351 #define MACRO X
352 int variable;
353 struct Struct {};
354 int function();
355 int X = ^
356 )cpp");
357 EXPECT_THAT(Results.items, Has("function", CompletionItemKind::Function));
358 EXPECT_THAT(Results.items, Has("variable", CompletionItemKind::Variable));
359 EXPECT_THAT(Results.items, Has("int", CompletionItemKind::Keyword));
360 EXPECT_THAT(Results.items, Has("Struct", CompletionItemKind::Class));
361 EXPECT_THAT(Results.items, Has("MACRO", CompletionItemKind::Text));
362
363 clangd::CodeCompleteOptions Opts;
364 Opts.EnableSnippets = true; // Needed for code patterns.
365
366 Results = completions("nam^");
367 EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
368}
369
Sam McCall9aad25f2017-12-05 07:20:26 +0000370} // namespace
371} // namespace clangd
372} // namespace clang