clang-tools  11.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"
13 #include "ConfigFragment.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 ==
58 }
59 
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:
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 
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:
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.
103  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 
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
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 
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:
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 
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 
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 
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 
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 
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 
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 
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
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 
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 
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.
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 
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 
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 
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 
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 
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 
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)
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 
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();
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.
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 
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();
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)
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
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::runFormatFile
llvm::Expected< tooling::Replacements > runFormatFile(ClangdServer &Server, PathRef File, StringRef Code)
Definition: SyncAPI.cpp:109
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:704
clang::clangd::CodeCompletion::Scope
std::string Scope
Definition: CodeComplete.h:160
CodeComplete.h
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::CodeCompleteOptions::RunParser
enum clang::clangd::CodeCompleteOptions::CodeCompletionParse RunParser
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:27
Location
Definition: Modularize.cpp:383
Path.h
clang::clangd::Context::current
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
clang::clangd::runCodeComplete
llvm::Expected< CodeCompleteResult > runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, clangd::CodeCompleteOptions Opts)
Definition: SyncAPI.cpp:72
clang::clangd::CompletionItemKind::Field
clang::clangd::CodeCompleteOptions::ParseIfReady
Run the parser if inputs (preamble) are ready.
Definition: CodeComplete.h:136
clang::clangd::ClangdServer::optsForTest
static Options optsForTest()
Definition: ClangdServer.cpp:114
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:92
clang::clangd::runAddDocument
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, llvm::StringRef Version, WantDiagnostics WantDiags, bool ForceRebuild)
Definition: SyncAPI.cpp:15
clang::clangd::Position::line
int line
Line position in a document (zero-based).
Definition: Protocol.h:146
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::testRoot
const char * testRoot()
Definition: TestFS.cpp:74
ClangdLSPServer.h
GlobalCompilationDatabase.h
clang::clangd::buildTestFS
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
Definition: TestFS.cpp:22
FS
MockFS FS
Definition: ClangdLSPServerTests.cpp:66
CommandLine
std::vector< llvm::StringRef > CommandLine
Definition: Serialization.cpp:388
TestFS.h
Threading.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
SyncAPI.h
clang::clangd::runLocateSymbolAt
llvm::Expected< std::vector< LocatedSymbol > > runLocateSymbolAt(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:87
clang::clangd::Position::character
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:151
clang::clangd::runFindDocumentHighlights
llvm::Expected< std::vector< DocumentHighlight > > runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:94
clang::clangd::CodeCompleteOptions::NeverParse
Always use text-based completion.
Definition: CodeComplete.h:138
EXPECT_ERROR
#define EXPECT_ERROR(expectedValue)
Definition: clangd/unittests/Matchers.h:118
Annotations.h
FilePaths
std::vector< std::string > FilePaths
Definition: IndexActionTests.cpp:108
clang::clangd::CompletionItemKind::File
clang::clangd::Empty
Definition: FuzzyMatch.h:42
clang::clangd::runDumpAST
std::string runDumpAST(ClangdServer &Server, PathRef File)
Definition: SyncAPI.cpp:115
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
clang::clangd::URIForFile::canonicalize
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition: Protocol.cpp:33
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
ClangdServer.h
clang::clangd::CodeCompletion::Name
std::string Name
Definition: CodeComplete.h:157
clang::clangd::runSignatureHelp
llvm::Expected< SignatureHelp > runSignatureHelp(ClangdServer &Server, PathRef File, Position Pos)
Definition: SyncAPI.cpp:79
clang::clangd::MockFS::Files
llvm::StringMap< std::string > Files
Definition: TestFS.h:41
Pos
Position Pos
Definition: SourceCode.cpp:649
URI.h
clang::clangd::CommandMangler::forTests
static CommandMangler forTests()
Definition: CompileCommands.cpp:185
ConfigFragment.h
Matchers.h
FileName
PathRef FileName
Definition: CodeComplete.cpp:1043
clang::clangd::Context::getExisting
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
clang::clangd::MessageType::Error
An error message.
clang::clangd::runRename
llvm::Expected< FileEdits > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &RenameOpts)
Definition: SyncAPI.cpp:100
clang::clangd::FileChangeType::Changed
The file got changed.
clang::clangd::config::DiagnosticCallback
llvm::function_ref< void(const llvm::SMDiagnostic &)> DiagnosticCallback
Used to report problems in parsing or interpreting a config.
Definition: ConfigProvider.h:47