1 //===-- ClangdTests.cpp - Clangd unit tests ---------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Annotations.h"
10 #include "ClangdLSPServer.h"
11 #include "ClangdServer.h"
12 #include "CodeComplete.h"
13 #include "GlobalCompilationDatabase.h"
14 #include "Matchers.h"
15 #include "SyncAPI.h"
16 #include "TestFS.h"
17 #include "Threading.h"
18 #include "URI.h"
19 #include "clang/Config/config.h"
20 #include "clang/Sema/CodeCompleteConsumer.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringMap.h"
23 #include "llvm/Support/Errc.h"
24 #include "llvm/Support/Path.h"
25 #include "llvm/Support/Regex.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include <algorithm>
29 #include <chrono>
30 #include <iostream>
31 #include <random>
32 #include <string>
33 #include <thread>
34 #include <vector>
35
36 namespace clang {
37 namespace clangd {
38
39 namespace {
40
41 using ::testing::ElementsAre;
42 using ::testing::Field;
43 using ::testing::Gt;
44 using ::testing::IsEmpty;
45 using ::testing::Pair;
46 using ::testing::UnorderedElementsAre;
47
48 MATCHER_P2(DeclAt, File, Range, "") {
49 return arg.PreferredDeclaration ==
50 Location{URIForFile::canonicalize(File, testRoot()), Range};
51 }
52
diagsContainErrors(const std::vector<Diag> & Diagnostics)53 bool diagsContainErrors(const std::vector<Diag> &Diagnostics) {
54 for (auto D : Diagnostics) {
55 if (D.Severity == DiagnosticsEngine::Error ||
56 D.Severity == DiagnosticsEngine::Fatal)
57 return true;
58 }
59 return false;
60 }
61
62 class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
63 public:
onDiagnosticsReady(PathRef File,std::vector<Diag> Diagnostics)64 void onDiagnosticsReady(PathRef File,
65 std::vector<Diag> Diagnostics) override {
66 bool HadError = diagsContainErrors(Diagnostics);
67 std::lock_guard<std::mutex> Lock(Mutex);
68 HadErrorInLastDiags = HadError;
69 }
70
hadErrorInLastDiags()71 bool hadErrorInLastDiags() {
72 std::lock_guard<std::mutex> Lock(Mutex);
73 return HadErrorInLastDiags;
74 }
75
76 private:
77 std::mutex Mutex;
78 bool HadErrorInLastDiags = false;
79 };
80
81 /// For each file, record whether the last published diagnostics contained at
82 /// least one error.
83 class MultipleErrorCheckingDiagConsumer : public DiagnosticsConsumer {
84 public:
onDiagnosticsReady(PathRef File,std::vector<Diag> Diagnostics)85 void onDiagnosticsReady(PathRef File,
86 std::vector<Diag> Diagnostics) override {
87 bool HadError = diagsContainErrors(Diagnostics);
88
89 std::lock_guard<std::mutex> Lock(Mutex);
90 LastDiagsHadError[File] = HadError;
91 }
92
93 /// Exposes all files consumed by onDiagnosticsReady in an unspecified order.
94 /// For each file, a bool value indicates whether the last diagnostics
95 /// contained an error.
filesWithDiags() const96 std::vector<std::pair<Path, bool>> filesWithDiags() const {
97 std::vector<std::pair<Path, bool>> Result;
98 std::lock_guard<std::mutex> Lock(Mutex);
99 for (const auto &It : LastDiagsHadError)
100 Result.emplace_back(It.first(), It.second);
101 return Result;
102 }
103
clear()104 void clear() {
105 std::lock_guard<std::mutex> Lock(Mutex);
106 LastDiagsHadError.clear();
107 }
108
109 private:
110 mutable std::mutex Mutex;
111 llvm::StringMap<bool> LastDiagsHadError;
112 };
113
114 /// Replaces all patterns of the form 0x123abc with spaces
replacePtrsInDump(std::string const & Dump)115 std::string replacePtrsInDump(std::string const &Dump) {
116 llvm::Regex RE("0x[0-9a-fA-F]+");
117 llvm::SmallVector<llvm::StringRef, 1> Matches;
118 llvm::StringRef Pending = Dump;
119
120 std::string Result;
121 while (RE.match(Pending, &Matches)) {
122 assert(Matches.size() == 1 && "Exactly one match expected");
123 auto MatchPos = Matches[0].data() - Pending.data();
124
125 Result += Pending.take_front(MatchPos);
126 Pending = Pending.drop_front(MatchPos + Matches[0].size());
127 }
128 Result += Pending;
129
130 return Result;
131 }
132
dumpASTWithoutMemoryLocs(ClangdServer & Server,PathRef File)133 std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
134 auto DumpWithMemLocs = runDumpAST(Server, File);
135 return replacePtrsInDump(DumpWithMemLocs);
136 }
137
138 class ClangdVFSTest : public ::testing::Test {
139 protected:
parseSourceAndDumpAST(PathRef SourceFileRelPath,llvm::StringRef SourceContents,std::vector<std::pair<PathRef,llvm::StringRef>> ExtraFiles={},bool ExpectErrors=false)140 std::string parseSourceAndDumpAST(
141 PathRef SourceFileRelPath, llvm::StringRef SourceContents,
142 std::vector<std::pair<PathRef, llvm::StringRef>> ExtraFiles = {},
143 bool ExpectErrors = false) {
144 MockFSProvider FS;
145 ErrorCheckingDiagConsumer DiagConsumer;
146 MockCompilationDatabase CDB;
147 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
148 for (const auto &FileWithContents : ExtraFiles)
149 FS.Files[testPath(FileWithContents.first)] = FileWithContents.second;
150
151 auto SourceFilename = testPath(SourceFileRelPath);
152 Server.addDocument(SourceFilename, SourceContents);
153 auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename);
154 EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
155 EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
156 return Result;
157 }
158 };
159
TEST_F(ClangdVFSTest,Parse)160 TEST_F(ClangdVFSTest, Parse) {
161 // FIXME: figure out a stable format for AST dumps, so that we can check the
162 // output of the dump itself is equal to the expected one, not just that it's
163 // different.
164 auto Empty = parseSourceAndDumpAST("foo.cpp", "", {});
165 auto OneDecl = parseSourceAndDumpAST("foo.cpp", "int a;", {});
166 auto SomeDecls = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;", {});
167 EXPECT_NE(Empty, OneDecl);
168 EXPECT_NE(Empty, SomeDecls);
169 EXPECT_NE(SomeDecls, OneDecl);
170
171 auto Empty2 = parseSourceAndDumpAST("foo.cpp", "");
172 auto OneDecl2 = parseSourceAndDumpAST("foo.cpp", "int a;");
173 auto SomeDecls2 = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
174 EXPECT_EQ(Empty, Empty2);
175 EXPECT_EQ(OneDecl, OneDecl2);
176 EXPECT_EQ(SomeDecls, SomeDecls2);
177 }
178
TEST_F(ClangdVFSTest,ParseWithHeader)179 TEST_F(ClangdVFSTest, ParseWithHeader) {
180 parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {},
181 /*ExpectErrors=*/true);
182 parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {{"foo.h", ""}},
183 /*ExpectErrors=*/false);
184
185 const auto SourceContents = R"cpp(
186 #include "foo.h"
187 int b = a;
188 )cpp";
189 parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", ""}},
190 /*ExpectErrors=*/true);
191 parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", "int a;"}},
192 /*ExpectErrors=*/false);
193 }
194
TEST_F(ClangdVFSTest,Reparse)195 TEST_F(ClangdVFSTest, Reparse) {
196 MockFSProvider FS;
197 ErrorCheckingDiagConsumer DiagConsumer;
198 MockCompilationDatabase CDB;
199 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
200
201 const auto SourceContents = R"cpp(
202 #include "foo.h"
203 int b = a;
204 )cpp";
205
206 auto FooCpp = testPath("foo.cpp");
207
208 FS.Files[testPath("foo.h")] = "int a;";
209 FS.Files[FooCpp] = SourceContents;
210
211 Server.addDocument(FooCpp, SourceContents);
212 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
213 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
214 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
215
216 Server.addDocument(FooCpp, "");
217 auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
218 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
219 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
220
221 Server.addDocument(FooCpp, SourceContents);
222 auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
223 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
224 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
225
226 EXPECT_EQ(DumpParse1, DumpParse2);
227 EXPECT_NE(DumpParse1, DumpParseEmpty);
228 }
229
TEST_F(ClangdVFSTest,ReparseOnHeaderChange)230 TEST_F(ClangdVFSTest, ReparseOnHeaderChange) {
231 MockFSProvider FS;
232 ErrorCheckingDiagConsumer DiagConsumer;
233 MockCompilationDatabase CDB;
234 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
235
236 const auto SourceContents = R"cpp(
237 #include "foo.h"
238 int b = a;
239 )cpp";
240
241 auto FooCpp = testPath("foo.cpp");
242 auto FooH = testPath("foo.h");
243
244 FS.Files[FooH] = "int a;";
245 FS.Files[FooCpp] = SourceContents;
246
247 Server.addDocument(FooCpp, SourceContents);
248 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
249 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
250 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
251
252 FS.Files[FooH] = "";
253 Server.addDocument(FooCpp, SourceContents);
254 auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp);
255 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
256 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
257
258 FS.Files[FooH] = "int a;";
259 Server.addDocument(FooCpp, SourceContents);
260 auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
261 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
262 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
263
264 EXPECT_EQ(DumpParse1, DumpParse2);
265 EXPECT_NE(DumpParse1, DumpParseDifferent);
266 }
267
TEST_F(ClangdVFSTest,PropagatesContexts)268 TEST_F(ClangdVFSTest, PropagatesContexts) {
269 static Key<int> Secret;
270 struct FSProvider : public FileSystemProvider {
271 IntrusiveRefCntPtr<llvm::vfs::FileSystem> getFileSystem() const override {
272 Got = Context::current().getExisting(Secret);
273 return buildTestFS({});
274 }
275 mutable int Got;
276 } FS;
277 struct DiagConsumer : public DiagnosticsConsumer {
278 void onDiagnosticsReady(PathRef File,
279 std::vector<Diag> Diagnostics) override {
280 Got = Context::current().getExisting(Secret);
281 }
282 int Got;
283 } DiagConsumer;
284 MockCompilationDatabase CDB;
285
286 // Verify that the context is plumbed to the FS provider and diagnostics.
287 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
288 {
289 WithContextValue Entrypoint(Secret, 42);
290 Server.addDocument(testPath("foo.cpp"), "void main(){}");
291 }
292 ASSERT_TRUE(Server.blockUntilIdleForTest());
293 EXPECT_EQ(FS.Got, 42);
294 EXPECT_EQ(DiagConsumer.Got, 42);
295 }
296
297 // Only enable this test on Unix
298 #ifdef LLVM_ON_UNIX
TEST_F(ClangdVFSTest,SearchLibDir)299 TEST_F(ClangdVFSTest, SearchLibDir) {
300 // Checks that searches for GCC installation is done through vfs.
301 MockFSProvider FS;
302 ErrorCheckingDiagConsumer DiagConsumer;
303 MockCompilationDatabase CDB;
304 CDB.ExtraClangFlags.insert(CDB.ExtraClangFlags.end(),
305 {"-xc++", "-target", "x86_64-linux-unknown",
306 "-m64", "--gcc-toolchain=/randomusr",
307 "-stdlib=libstdc++"});
308 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
309
310 // Just a random gcc version string
311 SmallString<8> Version("4.9.3");
312
313 // A lib dir for gcc installation
314 SmallString<64> LibDir("/randomusr/lib/gcc/x86_64-linux-gnu");
315 llvm::sys::path::append(LibDir, Version);
316
317 // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
318 // installation there.
319 SmallString<64> DummyLibFile;
320 llvm::sys::path::append(DummyLibFile, LibDir, "64", "crtbegin.o");
321 FS.Files[DummyLibFile] = "";
322
323 SmallString<64> IncludeDir("/randomusr/include/c++");
324 llvm::sys::path::append(IncludeDir, Version);
325
326 SmallString<64> StringPath;
327 llvm::sys::path::append(StringPath, IncludeDir, "string");
328 FS.Files[StringPath] = "class mock_string {};";
329
330 auto FooCpp = testPath("foo.cpp");
331 const auto SourceContents = R"cpp(
332 #include <string>
333 mock_string x;
334 )cpp";
335 FS.Files[FooCpp] = SourceContents;
336
337 runAddDocument(Server, FooCpp, SourceContents);
338 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
339
340 const auto SourceContentsWithError = R"cpp(
341 #include <string>
342 std::string x;
343 )cpp";
344 runAddDocument(Server, FooCpp, SourceContentsWithError);
345 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
346 }
347 #endif // LLVM_ON_UNIX
348
TEST_F(ClangdVFSTest,ForceReparseCompileCommand)349 TEST_F(ClangdVFSTest, ForceReparseCompileCommand) {
350 MockFSProvider FS;
351 ErrorCheckingDiagConsumer DiagConsumer;
352 MockCompilationDatabase CDB;
353 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
354
355 auto FooCpp = testPath("foo.cpp");
356 const auto SourceContents1 = R"cpp(
357 template <class T>
358 struct foo { T x; };
359 )cpp";
360 const auto SourceContents2 = R"cpp(
361 template <class T>
362 struct bar { T x; };
363 )cpp";
364
365 FS.Files[FooCpp] = "";
366
367 // First parse files in C mode and check they produce errors.
368 CDB.ExtraClangFlags = {"-xc"};
369 runAddDocument(Server, FooCpp, SourceContents1);
370 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
371 runAddDocument(Server, FooCpp, SourceContents2);
372 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
373
374 // Now switch to C++ mode.
375 CDB.ExtraClangFlags = {"-xc++"};
376 runAddDocument(Server, FooCpp, SourceContents2, WantDiagnostics::Auto);
377 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
378 // Subsequent addDocument calls should finish without errors too.
379 runAddDocument(Server, FooCpp, SourceContents1);
380 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
381 runAddDocument(Server, FooCpp, SourceContents2);
382 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
383 }
384
TEST_F(ClangdVFSTest,ForceReparseCompileCommandDefines)385 TEST_F(ClangdVFSTest, ForceReparseCompileCommandDefines) {
386 MockFSProvider FS;
387 ErrorCheckingDiagConsumer DiagConsumer;
388 MockCompilationDatabase CDB;
389 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
390
391 auto FooCpp = testPath("foo.cpp");
392 const auto SourceContents = R"cpp(
393 #ifdef WITH_ERROR
394 this
395 #endif
396
397 int main() { return 0; }
398 )cpp";
399 FS.Files[FooCpp] = "";
400
401 // Parse with define, we expect to see the errors.
402 CDB.ExtraClangFlags = {"-DWITH_ERROR"};
403 runAddDocument(Server, FooCpp, SourceContents);
404 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
405
406 // Parse without the define, no errors should be produced.
407 CDB.ExtraClangFlags = {};
408 runAddDocument(Server, FooCpp, SourceContents, WantDiagnostics::Auto);
409 ASSERT_TRUE(Server.blockUntilIdleForTest());
410 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
411 // Subsequent addDocument call should finish without errors too.
412 runAddDocument(Server, FooCpp, SourceContents);
413 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
414 }
415
416 // Test ClangdServer.reparseOpenedFiles.
TEST_F(ClangdVFSTest,ReparseOpenedFiles)417 TEST_F(ClangdVFSTest, ReparseOpenedFiles) {
418 Annotations FooSource(R"cpp(
419 #ifdef MACRO
420 static void $one[[bob]]() {}
421 #else
422 static void $two[[bob]]() {}
423 #endif
424
425 int main () { bo^b (); return 0; }
426 )cpp");
427
428 Annotations BarSource(R"cpp(
429 #ifdef MACRO
430 this is an error
431 #endif
432 )cpp");
433
434 Annotations BazSource(R"cpp(
435 int hello;
436 )cpp");
437
438 MockFSProvider FS;
439 MockCompilationDatabase CDB;
440 MultipleErrorCheckingDiagConsumer DiagConsumer;
441 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
442
443 auto FooCpp = testPath("foo.cpp");
444 auto BarCpp = testPath("bar.cpp");
445 auto BazCpp = testPath("baz.cpp");
446
447 FS.Files[FooCpp] = "";
448 FS.Files[BarCpp] = "";
449 FS.Files[BazCpp] = "";
450
451 CDB.ExtraClangFlags = {"-DMACRO=1"};
452 Server.addDocument(FooCpp, FooSource.code());
453 Server.addDocument(BarCpp, BarSource.code());
454 Server.addDocument(BazCpp, BazSource.code());
455 ASSERT_TRUE(Server.blockUntilIdleForTest());
456
457 EXPECT_THAT(DiagConsumer.filesWithDiags(),
458 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, true),
459 Pair(BazCpp, false)));
460
461 auto Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
462 EXPECT_TRUE(bool(Locations));
463 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("one"))));
464
465 // Undefine MACRO, close baz.cpp.
466 CDB.ExtraClangFlags.clear();
467 DiagConsumer.clear();
468 Server.removeDocument(BazCpp);
469 Server.addDocument(FooCpp, FooSource.code(), WantDiagnostics::Auto);
470 Server.addDocument(BarCpp, BarSource.code(), WantDiagnostics::Auto);
471 ASSERT_TRUE(Server.blockUntilIdleForTest());
472
473 EXPECT_THAT(DiagConsumer.filesWithDiags(),
474 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, false)));
475
476 Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
477 EXPECT_TRUE(bool(Locations));
478 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("two"))));
479 }
480
TEST_F(ClangdVFSTest,MemoryUsage)481 TEST_F(ClangdVFSTest, MemoryUsage) {
482 MockFSProvider FS;
483 ErrorCheckingDiagConsumer DiagConsumer;
484 MockCompilationDatabase CDB;
485 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
486
487 Path FooCpp = testPath("foo.cpp");
488 const auto SourceContents = R"cpp(
489 struct Something {
490 int method();
491 };
492 )cpp";
493 Path BarCpp = testPath("bar.cpp");
494
495 FS.Files[FooCpp] = "";
496 FS.Files[BarCpp] = "";
497
498 EXPECT_THAT(Server.getUsedBytesPerFile(), IsEmpty());
499
500 Server.addDocument(FooCpp, SourceContents);
501 Server.addDocument(BarCpp, SourceContents);
502 ASSERT_TRUE(Server.blockUntilIdleForTest());
503
504 EXPECT_THAT(Server.getUsedBytesPerFile(),
505 UnorderedElementsAre(Pair(FooCpp, Gt(0u)), Pair(BarCpp, Gt(0u))));
506
507 Server.removeDocument(FooCpp);
508 ASSERT_TRUE(Server.blockUntilIdleForTest());
509 EXPECT_THAT(Server.getUsedBytesPerFile(), ElementsAre(Pair(BarCpp, Gt(0u))));
510
511 Server.removeDocument(BarCpp);
512 ASSERT_TRUE(Server.blockUntilIdleForTest());
513 EXPECT_THAT(Server.getUsedBytesPerFile(), IsEmpty());
514 }
515
TEST_F(ClangdVFSTest,InvalidCompileCommand)516 TEST_F(ClangdVFSTest, InvalidCompileCommand) {
517 MockFSProvider FS;
518 ErrorCheckingDiagConsumer DiagConsumer;
519 MockCompilationDatabase CDB;
520
521 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
522
523 auto FooCpp = testPath("foo.cpp");
524 // clang cannot create CompilerInvocation if we pass two files in the
525 // CompileCommand. We pass the file in ExtraFlags once and CDB adds another
526 // one in getCompileCommand().
527 CDB.ExtraClangFlags.push_back(FooCpp);
528
529 // Clang can't parse command args in that case, but we shouldn't crash.
530 runAddDocument(Server, FooCpp, "int main() {}");
531
532 EXPECT_EQ(runDumpAST(Server, FooCpp), "<no-ast>");
533 EXPECT_ERROR(runLocateSymbolAt(Server, FooCpp, Position()));
534 EXPECT_ERROR(runFindDocumentHighlights(Server, FooCpp, Position()));
535 EXPECT_ERROR(runRename(Server, FooCpp, Position(), "new_name"));
536 // Identifier-based fallback completion.
537 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Position(),
538 clangd::CodeCompleteOptions()))
539 .Completions,
540 ElementsAre(Field(&CodeCompletion::Name, "int"),
541 Field(&CodeCompletion::Name, "main")));
542 auto SigHelp = runSignatureHelp(Server, FooCpp, Position());
543 ASSERT_TRUE(bool(SigHelp)) << "signatureHelp returned an error";
544 EXPECT_THAT(SigHelp->signatures, IsEmpty());
545 }
546
547 class ClangdThreadingTest : public ClangdVFSTest {};
548
TEST_F(ClangdThreadingTest,StressTest)549 TEST_F(ClangdThreadingTest, StressTest) {
550 // Without 'static' clang gives an error for a usage inside TestDiagConsumer.
551 static const unsigned FilesCount = 5;
552 const unsigned RequestsCount = 500;
553 // Blocking requests wait for the parsing to complete, they slow down the test
554 // dramatically, so they are issued rarely. Each
555 // BlockingRequestInterval-request will be a blocking one.
556 const unsigned BlockingRequestInterval = 40;
557
558 const auto SourceContentsWithoutErrors = R"cpp(
559 int a;
560 int b;
561 int c;
562 int d;
563 )cpp";
564
565 const auto SourceContentsWithErrors = R"cpp(
566 int a = x;
567 int b;
568 int c;
569 int d;
570 )cpp";
571
572 // Giving invalid line and column number should not crash ClangdServer, but
573 // just to make sure we're sometimes hitting the bounds inside the file we
574 // limit the intervals of line and column number that are generated.
575 unsigned MaxLineForFileRequests = 7;
576 unsigned MaxColumnForFileRequests = 10;
577
578 std::vector<std::string> FilePaths;
579 MockFSProvider FS;
580 for (unsigned I = 0; I < FilesCount; ++I) {
581 std::string Name = std::string("Foo") + std::to_string(I) + ".cpp";
582 FS.Files[Name] = "";
583 FilePaths.push_back(testPath(Name));
584 }
585
586 struct FileStat {
587 unsigned HitsWithoutErrors = 0;
588 unsigned HitsWithErrors = 0;
589 bool HadErrorsInLastDiags = false;
590 };
591
592 class TestDiagConsumer : public DiagnosticsConsumer {
593 public:
594 TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
595
596 void onDiagnosticsReady(PathRef File,
597 std::vector<Diag> Diagnostics) override {
598 StringRef FileIndexStr = llvm::sys::path::stem(File);
599 ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
600
601 unsigned long FileIndex = std::stoul(FileIndexStr.str());
602
603 bool HadError = diagsContainErrors(Diagnostics);
604
605 std::lock_guard<std::mutex> Lock(Mutex);
606 if (HadError)
607 Stats[FileIndex].HitsWithErrors++;
608 else
609 Stats[FileIndex].HitsWithoutErrors++;
610 Stats[FileIndex].HadErrorsInLastDiags = HadError;
611 }
612
613 std::vector<FileStat> takeFileStats() {
614 std::lock_guard<std::mutex> Lock(Mutex);
615 return std::move(Stats);
616 }
617
618 private:
619 std::mutex Mutex;
620 std::vector<FileStat> Stats;
621 };
622
623 struct RequestStats {
624 unsigned RequestsWithoutErrors = 0;
625 unsigned RequestsWithErrors = 0;
626 bool LastContentsHadErrors = false;
627 bool FileIsRemoved = true;
628 };
629
630 std::vector<RequestStats> ReqStats;
631 ReqStats.reserve(FilesCount);
632 for (unsigned FileIndex = 0; FileIndex < FilesCount; ++FileIndex)
633 ReqStats.emplace_back();
634
635 TestDiagConsumer DiagConsumer;
636 {
637 MockCompilationDatabase CDB;
638 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
639
640 // Prepare some random distributions for the test.
641 std::random_device RandGen;
642
643 std::uniform_int_distribution<unsigned> FileIndexDist(0, FilesCount - 1);
644 // Pass a text that contains compiler errors to addDocument in about 20% of
645 // all requests.
646 std::bernoulli_distribution ShouldHaveErrorsDist(0.2);
647 // Line and Column numbers for requests that need them.
648 std::uniform_int_distribution<int> LineDist(0, MaxLineForFileRequests);
649 std::uniform_int_distribution<int> ColumnDist(0, MaxColumnForFileRequests);
650
651 // Some helpers.
652 auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors) {
653 auto &Stats = ReqStats[FileIndex];
654
655 if (HadErrors)
656 ++Stats.RequestsWithErrors;
657 else
658 ++Stats.RequestsWithoutErrors;
659 Stats.LastContentsHadErrors = HadErrors;
660 Stats.FileIsRemoved = false;
661 };
662
663 auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex) {
664 auto &Stats = ReqStats[FileIndex];
665
666 Stats.FileIsRemoved = true;
667 };
668
669 auto AddDocument = [&](unsigned FileIndex, bool SkipCache) {
670 bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen);
671 Server.addDocument(FilePaths[FileIndex],
672 ShouldHaveErrors ? SourceContentsWithErrors
673 : SourceContentsWithoutErrors,
674 WantDiagnostics::Auto);
675 UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors);
676 };
677
678 // Various requests that we would randomly run.
679 auto AddDocumentRequest = [&]() {
680 unsigned FileIndex = FileIndexDist(RandGen);
681 AddDocument(FileIndex, /*SkipCache=*/false);
682 };
683
684 auto ForceReparseRequest = [&]() {
685 unsigned FileIndex = FileIndexDist(RandGen);
686 AddDocument(FileIndex, /*SkipCache=*/true);
687 };
688
689 auto RemoveDocumentRequest = [&]() {
690 unsigned FileIndex = FileIndexDist(RandGen);
691 // Make sure we don't violate the ClangdServer's contract.
692 if (ReqStats[FileIndex].FileIsRemoved)
693 AddDocument(FileIndex, /*SkipCache=*/false);
694
695 Server.removeDocument(FilePaths[FileIndex]);
696 UpdateStatsOnRemoveDocument(FileIndex);
697 };
698
699 auto CodeCompletionRequest = [&]() {
700 unsigned FileIndex = FileIndexDist(RandGen);
701 // Make sure we don't violate the ClangdServer's contract.
702 if (ReqStats[FileIndex].FileIsRemoved)
703 AddDocument(FileIndex, /*SkipCache=*/false);
704
705 Position Pos;
706 Pos.line = LineDist(RandGen);
707 Pos.character = ColumnDist(RandGen);
708 // FIXME(ibiryukov): Also test async completion requests.
709 // Simply putting CodeCompletion into async requests now would make
710 // tests slow, since there's no way to cancel previous completion
711 // requests as opposed to AddDocument/RemoveDocument, which are implicitly
712 // cancelled by any subsequent AddDocument/RemoveDocument request to the
713 // same file.
714 cantFail(runCodeComplete(Server, FilePaths[FileIndex], Pos,
715 clangd::CodeCompleteOptions()));
716 };
717
718 auto LocateSymbolRequest = [&]() {
719 unsigned FileIndex = FileIndexDist(RandGen);
720 // Make sure we don't violate the ClangdServer's contract.
721 if (ReqStats[FileIndex].FileIsRemoved)
722 AddDocument(FileIndex, /*SkipCache=*/false);
723
724 Position Pos;
725 Pos.line = LineDist(RandGen);
726 Pos.character = ColumnDist(RandGen);
727
728 ASSERT_TRUE(!!runLocateSymbolAt(Server, FilePaths[FileIndex], Pos));
729 };
730
731 std::vector<std::function<void()>> AsyncRequests = {
732 AddDocumentRequest, ForceReparseRequest, RemoveDocumentRequest};
733 std::vector<std::function<void()>> BlockingRequests = {
734 CodeCompletionRequest, LocateSymbolRequest};
735
736 // Bash requests to ClangdServer in a loop.
737 std::uniform_int_distribution<int> AsyncRequestIndexDist(
738 0, AsyncRequests.size() - 1);
739 std::uniform_int_distribution<int> BlockingRequestIndexDist(
740 0, BlockingRequests.size() - 1);
741 for (unsigned I = 1; I <= RequestsCount; ++I) {
742 if (I % BlockingRequestInterval != 0) {
743 // Issue an async request most of the time. It should be fast.
744 unsigned RequestIndex = AsyncRequestIndexDist(RandGen);
745 AsyncRequests[RequestIndex]();
746 } else {
747 // Issue a blocking request once in a while.
748 auto RequestIndex = BlockingRequestIndexDist(RandGen);
749 BlockingRequests[RequestIndex]();
750 }
751 }
752 ASSERT_TRUE(Server.blockUntilIdleForTest());
753 }
754
755 // Check some invariants about the state of the program.
756 std::vector<FileStat> Stats = DiagConsumer.takeFileStats();
757 for (unsigned I = 0; I < FilesCount; ++I) {
758 if (!ReqStats[I].FileIsRemoved) {
759 ASSERT_EQ(Stats[I].HadErrorsInLastDiags,
760 ReqStats[I].LastContentsHadErrors);
761 }
762
763 ASSERT_LE(Stats[I].HitsWithErrors, ReqStats[I].RequestsWithErrors);
764 ASSERT_LE(Stats[I].HitsWithoutErrors, ReqStats[I].RequestsWithoutErrors);
765 }
766 }
767
TEST_F(ClangdVFSTest,CheckSourceHeaderSwitch)768 TEST_F(ClangdVFSTest, CheckSourceHeaderSwitch) {
769 MockFSProvider FS;
770 ErrorCheckingDiagConsumer DiagConsumer;
771 MockCompilationDatabase CDB;
772 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
773
774 auto SourceContents = R"cpp(
775 #include "foo.h"
776 int b = a;
777 )cpp";
778
779 auto FooCpp = testPath("foo.cpp");
780 auto FooH = testPath("foo.h");
781 auto Invalid = testPath("main.cpp");
782
783 FS.Files[FooCpp] = SourceContents;
784 FS.Files[FooH] = "int a;";
785 FS.Files[Invalid] = "int main() { \n return 0; \n }";
786
787 Optional<Path> PathResult = Server.switchSourceHeader(FooCpp);
788 EXPECT_TRUE(PathResult.hasValue());
789 ASSERT_EQ(PathResult.getValue(), FooH);
790
791 PathResult = Server.switchSourceHeader(FooH);
792 EXPECT_TRUE(PathResult.hasValue());
793 ASSERT_EQ(PathResult.getValue(), FooCpp);
794
795 SourceContents = R"c(
796 #include "foo.HH"
797 int b = a;
798 )c";
799
800 // Test with header file in capital letters and different extension, source
801 // file with different extension
802 auto FooC = testPath("bar.c");
803 auto FooHH = testPath("bar.HH");
804
805 FS.Files[FooC] = SourceContents;
806 FS.Files[FooHH] = "int a;";
807
808 PathResult = Server.switchSourceHeader(FooC);
809 EXPECT_TRUE(PathResult.hasValue());
810 ASSERT_EQ(PathResult.getValue(), FooHH);
811
812 // Test with both capital letters
813 auto Foo2C = testPath("foo2.C");
814 auto Foo2HH = testPath("foo2.HH");
815 FS.Files[Foo2C] = SourceContents;
816 FS.Files[Foo2HH] = "int a;";
817
818 PathResult = Server.switchSourceHeader(Foo2C);
819 EXPECT_TRUE(PathResult.hasValue());
820 ASSERT_EQ(PathResult.getValue(), Foo2HH);
821
822 // Test with source file as capital letter and .hxx header file
823 auto Foo3C = testPath("foo3.C");
824 auto Foo3HXX = testPath("foo3.hxx");
825
826 SourceContents = R"c(
827 #include "foo3.hxx"
828 int b = a;
829 )c";
830
831 FS.Files[Foo3C] = SourceContents;
832 FS.Files[Foo3HXX] = "int a;";
833
834 PathResult = Server.switchSourceHeader(Foo3C);
835 EXPECT_TRUE(PathResult.hasValue());
836 ASSERT_EQ(PathResult.getValue(), Foo3HXX);
837
838 // Test if asking for a corresponding file that doesn't exist returns an empty
839 // string.
840 PathResult = Server.switchSourceHeader(Invalid);
841 EXPECT_FALSE(PathResult.hasValue());
842 }
843
TEST_F(ClangdThreadingTest,NoConcurrentDiagnostics)844 TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) {
845 class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer {
846 public:
847 std::atomic<int> Count = {0};
848
849 NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
850 : StartSecondReparse(std::move(StartSecondReparse)) {}
851
852 void onDiagnosticsReady(PathRef, std::vector<Diag>) override {
853 ++Count;
854 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
855 ASSERT_TRUE(Lock.owns_lock())
856 << "Detected concurrent onDiagnosticsReady calls for the same file.";
857
858 // If we started the second parse immediately, it might cancel the first.
859 // So we don't allow it to start until the first has delivered diags...
860 if (FirstRequest) {
861 FirstRequest = false;
862 StartSecondReparse.set_value();
863 // ... but then we wait long enough that the callbacks would overlap.
864 std::this_thread::sleep_for(std::chrono::milliseconds(50));
865 }
866 }
867
868 private:
869 std::mutex Mutex;
870 bool FirstRequest = true;
871 std::promise<void> StartSecondReparse;
872 };
873
874 const auto SourceContentsWithoutErrors = R"cpp(
875 int a;
876 int b;
877 int c;
878 int d;
879 )cpp";
880
881 const auto SourceContentsWithErrors = R"cpp(
882 int a = x;
883 int b;
884 int c;
885 int d;
886 )cpp";
887
888 auto FooCpp = testPath("foo.cpp");
889 MockFSProvider FS;
890 FS.Files[FooCpp] = "";
891
892 std::promise<void> StartSecondPromise;
893 std::future<void> StartSecond = StartSecondPromise.get_future();
894
895 NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise));
896 MockCompilationDatabase CDB;
897 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
898 Server.addDocument(FooCpp, SourceContentsWithErrors);
899 StartSecond.wait();
900 Server.addDocument(FooCpp, SourceContentsWithoutErrors);
901 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
902 ASSERT_EQ(DiagConsumer.Count, 2); // Sanity check - we actually ran both?
903 }
904
TEST_F(ClangdVFSTest,FormatCode)905 TEST_F(ClangdVFSTest, FormatCode) {
906 MockFSProvider FS;
907 ErrorCheckingDiagConsumer DiagConsumer;
908 MockCompilationDatabase CDB;
909 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
910
911 auto Path = testPath("foo.cpp");
912 std::string Code = R"cpp(
913 #include "x.h"
914 #include "y.h"
915
916 void f( ) {}
917 )cpp";
918 std::string Expected = R"cpp(
919 #include "x.h"
920 #include "y.h"
921
922 void f() {}
923 )cpp";
924 FS.Files[Path] = Code;
925 runAddDocument(Server, Path, Code);
926
927 auto Replaces = Server.formatFile(Code, Path);
928 EXPECT_TRUE(static_cast<bool>(Replaces));
929 auto Changed = tooling::applyAllReplacements(Code, *Replaces);
930 EXPECT_TRUE(static_cast<bool>(Changed));
931 EXPECT_EQ(Expected, *Changed);
932 }
933
TEST_F(ClangdVFSTest,ChangedHeaderFromISystem)934 TEST_F(ClangdVFSTest, ChangedHeaderFromISystem) {
935 MockFSProvider FS;
936 ErrorCheckingDiagConsumer DiagConsumer;
937 MockCompilationDatabase CDB;
938 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
939
940 auto SourcePath = testPath("source/foo.cpp");
941 auto HeaderPath = testPath("headers/foo.h");
942 FS.Files[HeaderPath] = "struct X { int bar; };";
943 Annotations Code(R"cpp(
944 #include "foo.h"
945
946 int main() {
947 X().ba^
948 })cpp");
949 CDB.ExtraClangFlags.push_back("-xc++");
950 CDB.ExtraClangFlags.push_back("-isystem" + testPath("headers"));
951
952 runAddDocument(Server, SourcePath, Code.code());
953 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
954 clangd::CodeCompleteOptions()))
955 .Completions;
956 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar")));
957 // Update the header and rerun addDocument to make sure we get the updated
958 // files.
959 FS.Files[HeaderPath] = "struct X { int bar; int baz; };";
960 runAddDocument(Server, SourcePath, Code.code());
961 Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
962 clangd::CodeCompleteOptions()))
963 .Completions;
964 // We want to make sure we see the updated version.
965 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar"),
966 Field(&CodeCompletion::Name, "baz")));
967 }
968
969 // FIXME(ioeric): make this work for windows again.
970 #ifndef _WIN32
971 // Check that running code completion doesn't stat() a bunch of files from the
972 // preamble again. (They should be using the preamble's stat-cache)
TEST(ClangdTests,PreambleVFSStatCache)973 TEST(ClangdTests, PreambleVFSStatCache) {
974 class ListenStatsFSProvider : public FileSystemProvider {
975 public:
976 ListenStatsFSProvider(llvm::StringMap<unsigned> &CountStats)
977 : CountStats(CountStats) {}
978
979 IntrusiveRefCntPtr<llvm::vfs::FileSystem> getFileSystem() const override {
980 class ListenStatVFS : public llvm::vfs::ProxyFileSystem {
981 public:
982 ListenStatVFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
983 llvm::StringMap<unsigned> &CountStats)
984 : ProxyFileSystem(std::move(FS)), CountStats(CountStats) {}
985
986 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
987 openFileForRead(const Twine &Path) override {
988 ++CountStats[llvm::sys::path::filename(Path.str())];
989 return ProxyFileSystem::openFileForRead(Path);
990 }
991 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
992 ++CountStats[llvm::sys::path::filename(Path.str())];
993 return ProxyFileSystem::status(Path);
994 }
995
996 private:
997 llvm::StringMap<unsigned> &CountStats;
998 };
999
1000 return IntrusiveRefCntPtr<ListenStatVFS>(
1001 new ListenStatVFS(buildTestFS(Files), CountStats));
1002 }
1003
1004 // If relative paths are used, they are resolved with testPath().
1005 llvm::StringMap<std::string> Files;
1006 llvm::StringMap<unsigned> &CountStats;
1007 };
1008
1009 llvm::StringMap<unsigned> CountStats;
1010 ListenStatsFSProvider FS(CountStats);
1011 ErrorCheckingDiagConsumer DiagConsumer;
1012 MockCompilationDatabase CDB;
1013 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
1014
1015 auto SourcePath = testPath("foo.cpp");
1016 auto HeaderPath = testPath("foo.h");
1017 FS.Files[HeaderPath] = "struct TestSym {};";
1018 Annotations Code(R"cpp(
1019 #include "foo.h"
1020
1021 int main() {
1022 TestSy^
1023 })cpp");
1024
1025 runAddDocument(Server, SourcePath, Code.code());
1026
1027 unsigned Before = CountStats["foo.h"];
1028 EXPECT_GT(Before, 0u);
1029 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
1030 clangd::CodeCompleteOptions()))
1031 .Completions;
1032 EXPECT_EQ(CountStats["foo.h"], Before);
1033 EXPECT_THAT(Completions,
1034 ElementsAre(Field(&CodeCompletion::Name, "TestSym")));
1035 }
1036 #endif
1037
TEST_F(ClangdVFSTest,FlagsWithPlugins)1038 TEST_F(ClangdVFSTest, FlagsWithPlugins) {
1039 MockFSProvider FS;
1040 ErrorCheckingDiagConsumer DiagConsumer;
1041 MockCompilationDatabase CDB;
1042 CDB.ExtraClangFlags = {
1043 "-Xclang",
1044 "-add-plugin",
1045 "-Xclang",
1046 "random-plugin",
1047 };
1048 OverlayCDB OCDB(&CDB);
1049 ClangdServer Server(OCDB, FS, DiagConsumer, ClangdServer::optsForTest());
1050
1051 auto FooCpp = testPath("foo.cpp");
1052 const auto SourceContents = "int main() { return 0; }";
1053 FS.Files[FooCpp] = FooCpp;
1054 Server.addDocument(FooCpp, SourceContents);
1055 auto Result = dumpASTWithoutMemoryLocs(Server, FooCpp);
1056 EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
1057 EXPECT_NE(Result, "<no-ast>");
1058 }
1059
TEST_F(ClangdVFSTest,FallbackWhenPreambleIsNotReady)1060 TEST_F(ClangdVFSTest, FallbackWhenPreambleIsNotReady) {
1061 MockFSProvider FS;
1062 ErrorCheckingDiagConsumer DiagConsumer;
1063 MockCompilationDatabase CDB;
1064 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
1065
1066 auto FooCpp = testPath("foo.cpp");
1067 Annotations Code(R"cpp(
1068 namespace ns { int xyz; }
1069 using namespace ns;
1070 int main() {
1071 xy^
1072 })cpp");
1073 FS.Files[FooCpp] = FooCpp;
1074
1075 auto Opts = clangd::CodeCompleteOptions();
1076 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1077
1078 // This will make compile command broken and preamble absent.
1079 CDB.ExtraClangFlags = {"yolo.cc"};
1080 Server.addDocument(FooCpp, Code.code());
1081 ASSERT_TRUE(Server.blockUntilIdleForTest());
1082 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1083 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1084 // Identifier-based fallback completion doesn't know about "symbol" scope.
1085 EXPECT_THAT(Res.Completions,
1086 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1087 Field(&CodeCompletion::Scope, ""))));
1088
1089 // Make the compile command work again.
1090 CDB.ExtraClangFlags = {"-std=c++11"};
1091 Server.addDocument(FooCpp, Code.code());
1092 ASSERT_TRUE(Server.blockUntilIdleForTest());
1093 EXPECT_THAT(
1094 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1095 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1096 Field(&CodeCompletion::Scope, "ns::"))));
1097
1098 // Now force identifier-based completion.
1099 Opts.RunParser = CodeCompleteOptions::NeverParse;
1100 EXPECT_THAT(
1101 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1102 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1103 Field(&CodeCompletion::Scope, ""))));
1104 }
1105
TEST_F(ClangdVFSTest,FallbackWhenWaitingForCompileCommand)1106 TEST_F(ClangdVFSTest, FallbackWhenWaitingForCompileCommand) {
1107 MockFSProvider FS;
1108 ErrorCheckingDiagConsumer DiagConsumer;
1109 // Returns compile command only when notified.
1110 class DelayedCompilationDatabase : public GlobalCompilationDatabase {
1111 public:
1112 DelayedCompilationDatabase(Notification &CanReturnCommand)
1113 : CanReturnCommand(CanReturnCommand) {}
1114
1115 llvm::Optional<tooling::CompileCommand>
1116 getCompileCommand(PathRef File) const override {
1117 // FIXME: make this timeout and fail instead of waiting forever in case
1118 // something goes wrong.
1119 CanReturnCommand.wait();
1120 auto FileName = llvm::sys::path::filename(File);
1121 std::vector<std::string> CommandLine = {"clangd", "-ffreestanding", File};
1122 return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
1123 FileName, std::move(CommandLine), "")};
1124 }
1125
1126 std::vector<std::string> ExtraClangFlags;
1127
1128 private:
1129 Notification &CanReturnCommand;
1130 };
1131
1132 Notification CanReturnCommand;
1133 DelayedCompilationDatabase CDB(CanReturnCommand);
1134 ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
1135
1136 auto FooCpp = testPath("foo.cpp");
1137 Annotations Code(R"cpp(
1138 namespace ns { int xyz; }
1139 using namespace ns;
1140 int main() {
1141 xy^
1142 })cpp");
1143 FS.Files[FooCpp] = FooCpp;
1144 Server.addDocument(FooCpp, Code.code());
1145
1146 // Sleep for some time to make sure code completion is not run because update
1147 // hasn't been scheduled.
1148 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1149 auto Opts = clangd::CodeCompleteOptions();
1150 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1151
1152 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1153 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1154
1155 CanReturnCommand.notify();
1156 ASSERT_TRUE(Server.blockUntilIdleForTest());
1157 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
1158 clangd::CodeCompleteOptions()))
1159 .Completions,
1160 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1161 Field(&CodeCompletion::Scope, "ns::"))));
1162 }
1163
1164 } // namespace
1165 } // namespace clangd
1166 } // namespace clang
1167