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