clang-tools  9.0.0
ClangdTests.cpp
Go to the documentation of this file.
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"
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 ==
51 }
52 
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:
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 
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:
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.
96  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 
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
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 
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:
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 
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 
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 
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 
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 
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
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 
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 
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.
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 
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 
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 
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,
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 
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 
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 
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 
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)
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 
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 
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 
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
Always use text-based completion.
Definition: CodeComplete.h:131
llvm::Expected< CodeCompleteResult > runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, clangd::CodeCompleteOptions Opts)
Definition: SyncAPI.cpp:73
std::string runDumpAST(ClangdServer &Server, PathRef File)
Definition: SyncAPI.cpp:109
llvm::Expected< SignatureHelp > runSignatureHelp(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:80
llvm::Expected< std::vector< LocatedSymbol > > runLocateSymbolAt(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:88
#define EXPECT_ERROR(expectedValue)
Run the parser if inputs (preamble) are ready.
Definition: CodeComplete.h:129
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
MockFSProvider FS
static Options optsForTest()
std::vector< llvm::StringRef > CommandLine
MockFSProvider FSProvider
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
Definition: TestFS.cpp:22
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition: Protocol.cpp:32
static constexpr llvm::StringLiteral Name
const char * testRoot()
Definition: TestFS.cpp:74
const Decl * D
Definition: XRefs.cpp:868
std::vector< std::string > FilePaths
PathRef FileName
const Type & getExisting(const Key< Type > &Key) const
A helper to get a reference to a Key that must exist in the map.
Definition: Context.h:111
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangdServer Server
llvm::Expected< std::vector< DocumentHighlight > > runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:95
CharSourceRange Range
SourceRange for the file name.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
std::vector< const char * > Expected
IgnoreDiagnostics DiagConsumer
llvm::Expected< std::vector< TextEdit > > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName)
Definition: SyncAPI.cpp:101
Diagnostics must not be generated for this snapshot.
llvm::StringMap< std::string > Files
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
Definition: SyncAPI.cpp:15