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