blob: 8f2605d800afc31a5642d81205f498502acd9d8d [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.
Ilya Biryukov9b5ffc22017-12-12 12:56:46 +0000234 EXPECT_THAT(Results.items,
235 AllOf(Has("local_var"), Has("LocalClass"),
236 Contains(Kind(CompletionItemKind::Snippet))));
Sam McCallf6ae3232017-12-05 20:11:29 +0000237 // Check documentation.
Sam McCall44fdcec22017-12-08 15:00:59 +0000238 EXPECT_IFF(Opts.IncludeBriefComments, Results.items,
239 Contains(IsDocumented()));
Sam McCallf6ae3232017-12-05 20:11:29 +0000240}
241
242TEST(CompletionTest, CompletionOptions) {
243 clangd::CodeCompleteOptions Opts;
Sam McCall9aad25f2017-12-05 07:20:26 +0000244 for (bool IncludeMacros : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000245 Opts.IncludeMacros = IncludeMacros;
Sam McCall9aad25f2017-12-05 07:20:26 +0000246 for (bool IncludeGlobals : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000247 Opts.IncludeGlobals = IncludeGlobals;
Sam McCall9aad25f2017-12-05 07:20:26 +0000248 for (bool IncludeBriefComments : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000249 Opts.IncludeBriefComments = IncludeBriefComments;
Sam McCall9aad25f2017-12-05 07:20:26 +0000250 for (bool EnableSnippets : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000251 Opts.EnableSnippets = EnableSnippets;
Sam McCall9aad25f2017-12-05 07:20:26 +0000252 for (bool IncludeCodePatterns : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000253 Opts.IncludeCodePatterns = IncludeCodePatterns;
Sam McCall9aad25f2017-12-05 07:20:26 +0000254 for (bool IncludeIneligibleResults : {true, false}) {
Sam McCallf6ae3232017-12-05 20:11:29 +0000255 Opts.IncludeIneligibleResults = IncludeIneligibleResults;
256 TestAfterDotCompletion(Opts);
257 TestGlobalScopeCompletion(Opts);
Sam McCall9aad25f2017-12-05 07:20:26 +0000258 }
259 }
260 }
261 }
262 }
263 }
264}
265
Sam McCallf6ae3232017-12-05 20:11:29 +0000266// Check code completion works when the file contents are overridden.
267TEST(CompletionTest, CheckContentsOverride) {
268 MockFSProvider FS;
269 IgnoreDiagnostics DiagConsumer;
270 MockCompilationDatabase CDB;
271 ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
272 /*StorePreamblesInMemory=*/true,
273 EmptyLogger::getInstance());
274 auto File = getVirtualTestFilePath("foo.cpp");
275 Server.addDocument(File, "ignored text!");
276
277 auto Example = parseTextMarker("int cbc; int b = ^;");
278 auto Results =
279 Server
280 .codeComplete(File, Example.MarkerPos, clangd::CodeCompleteOptions(),
281 StringRef(Example.Text))
282 .get()
283 .Value;
284 EXPECT_THAT(Results.items, Contains(Named("cbc")));
285}
286
Sam McCall44fdcec22017-12-08 15:00:59 +0000287TEST(CompletionTest, Priorities) {
288 auto Internal = completions(R"cpp(
289 class Foo {
290 public: void pub();
291 protected: void prot();
292 private: void priv();
293 };
294 void Foo::pub() { this->^ }
295 )cpp");
296 EXPECT_THAT(Internal.items,
297 HasSubsequence(Named("priv"), Named("prot"), Named("pub")));
298
299 auto External = completions(R"cpp(
300 class Foo {
301 public: void pub();
302 protected: void prot();
303 private: void priv();
304 };
305 void test() {
306 Foo F;
307 F.^
308 }
309 )cpp");
310 EXPECT_THAT(External.items,
311 AllOf(Has("pub"), Not(Has("prot")), Not(Has("priv"))));
312}
313
314TEST(CompletionTest, Qualifiers) {
315 auto Results = completions(R"cpp(
316 class Foo {
317 public: int foo() const;
318 int bar() const;
319 };
320 class Bar : public Foo {
321 int foo() const;
322 };
323 void test() { Bar().^ }
324 )cpp");
325 EXPECT_THAT(Results.items, HasSubsequence(Labeled("bar() const"),
326 Labeled("Foo::foo() const")));
327 EXPECT_THAT(Results.items, Not(Contains(Labeled("foo() const")))); // private
328}
329
330TEST(CompletionTest, Snippets) {
331 clangd::CodeCompleteOptions Opts;
332 Opts.EnableSnippets = true;
333 auto Results = completions(
334 R"cpp(
335 struct fake {
336 int a;
337 int f(int i, const float f) const;
338 };
339 int main() {
340 fake f;
341 f.^
342 }
343 )cpp",
344 Opts);
345 EXPECT_THAT(Results.items,
346 HasSubsequence(PlainText("a"),
347 Snippet("f(${1:int i}, ${2:const float f})")));
348}
349
350TEST(CompletionTest, Kinds) {
351 auto Results = completions(R"cpp(
352 #define MACRO X
353 int variable;
354 struct Struct {};
355 int function();
356 int X = ^
357 )cpp");
358 EXPECT_THAT(Results.items, Has("function", CompletionItemKind::Function));
359 EXPECT_THAT(Results.items, Has("variable", CompletionItemKind::Variable));
360 EXPECT_THAT(Results.items, Has("int", CompletionItemKind::Keyword));
361 EXPECT_THAT(Results.items, Has("Struct", CompletionItemKind::Class));
362 EXPECT_THAT(Results.items, Has("MACRO", CompletionItemKind::Text));
363
364 clangd::CodeCompleteOptions Opts;
365 Opts.EnableSnippets = true; // Needed for code patterns.
366
367 Results = completions("nam^");
368 EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
369}
370
Sam McCall9aad25f2017-12-05 07:20:26 +0000371} // namespace
372} // namespace clangd
373} // namespace clang