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