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.ConfigProvider = &CfgProvider;
352 OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{},
353 tooling::ArgumentsAdjuster(CommandMangler::forTests()));
354 MockFS FS;
355 ClangdServer Server(CDB, FS, Opts);
356 // foo.cc sees the expected definition, as FOO is defined.
357 Server.addDocument(testPath("foo.cc"), Example.code());
358 auto Result = runLocateSymbolAt(Server, testPath("foo.cc"), Example.point());
359 ASSERT_TRUE(bool(Result)) << Result.takeError();
360 ASSERT_THAT(*Result, SizeIs(1));
361 EXPECT_EQ(Result->front().PreferredDeclaration.range, Example.range());
362 // bar.cc gets a different result, as FOO is not defined.
363 Server.addDocument(testPath("bar.cc"), Example.code());
364 Result = runLocateSymbolAt(Server, testPath("bar.cc"), Example.point());
365 ASSERT_TRUE(bool(Result)) << Result.takeError();
366 ASSERT_THAT(*Result, SizeIs(1));
367 EXPECT_NE(Result->front().PreferredDeclaration.range, Example.range());
368 }
369
TEST(ClangdServerTest,PropagatesVersion)370 TEST(ClangdServerTest, PropagatesVersion) {
371 MockCompilationDatabase CDB;
372 MockFS FS;
373 struct Callbacks : public ClangdServer::Callbacks {
374 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
375 std::vector<Diag> Diagnostics) override {
376 Got = Version.str();
377 }
378 std::string Got = "";
379 } Callbacks;
380
381 // Verify that the version is plumbed to diagnostics.
382 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &Callbacks);
383 runAddDocument(Server, testPath("foo.cpp"), "void main(){}", "42");
384 EXPECT_EQ(Callbacks.Got, "42");
385 }
386
387 // Only enable this test on Unix
388 #ifdef LLVM_ON_UNIX
TEST(ClangdServerTest,SearchLibDir)389 TEST(ClangdServerTest, SearchLibDir) {
390 // Checks that searches for GCC installation is done through vfs.
391 MockFS FS;
392 ErrorCheckingCallbacks DiagConsumer;
393 MockCompilationDatabase CDB;
394 CDB.ExtraClangFlags.insert(CDB.ExtraClangFlags.end(),
395 {"-xc++", "-target", "x86_64-linux-unknown",
396 "-m64", "--gcc-toolchain=/randomusr",
397 "-stdlib=libstdc++"});
398 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
399
400 // Just a random gcc version string
401 SmallString<8> Version("4.9.3");
402
403 // A lib dir for gcc installation
404 SmallString<64> LibDir("/randomusr/lib/gcc/x86_64-linux-gnu");
405 llvm::sys::path::append(LibDir, Version);
406
407 // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
408 // installation there.
409 SmallString<64> DummyLibFile;
410 llvm::sys::path::append(DummyLibFile, LibDir, "64", "crtbegin.o");
411 FS.Files[DummyLibFile] = "";
412
413 SmallString<64> IncludeDir("/randomusr/include/c++");
414 llvm::sys::path::append(IncludeDir, Version);
415
416 SmallString<64> StringPath;
417 llvm::sys::path::append(StringPath, IncludeDir, "string");
418 FS.Files[StringPath] = "class mock_string {};";
419
420 auto FooCpp = testPath("foo.cpp");
421 const auto SourceContents = R"cpp(
422 #include <string>
423 mock_string x;
424 )cpp";
425 FS.Files[FooCpp] = SourceContents;
426
427 runAddDocument(Server, FooCpp, SourceContents);
428 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
429
430 const auto SourceContentsWithError = R"cpp(
431 #include <string>
432 std::string x;
433 )cpp";
434 runAddDocument(Server, FooCpp, SourceContentsWithError);
435 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
436 }
437 #endif // LLVM_ON_UNIX
438
TEST(ClangdServerTest,ForceReparseCompileCommand)439 TEST(ClangdServerTest, ForceReparseCompileCommand) {
440 MockFS FS;
441 ErrorCheckingCallbacks DiagConsumer;
442 MockCompilationDatabase CDB;
443 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
444
445 auto FooCpp = testPath("foo.cpp");
446 const auto SourceContents1 = R"cpp(
447 template <class T>
448 struct foo { T x; };
449 )cpp";
450 const auto SourceContents2 = R"cpp(
451 template <class T>
452 struct bar { T x; };
453 )cpp";
454
455 FS.Files[FooCpp] = "";
456
457 // First parse files in C mode and check they produce errors.
458 CDB.ExtraClangFlags = {"-xc"};
459 runAddDocument(Server, FooCpp, SourceContents1);
460 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
461 runAddDocument(Server, FooCpp, SourceContents2);
462 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
463
464 // Now switch to C++ mode.
465 CDB.ExtraClangFlags = {"-xc++"};
466 runAddDocument(Server, FooCpp, SourceContents2);
467 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
468 // Subsequent addDocument calls should finish without errors too.
469 runAddDocument(Server, FooCpp, SourceContents1);
470 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
471 runAddDocument(Server, FooCpp, SourceContents2);
472 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
473 }
474
TEST(ClangdServerTest,ForceReparseCompileCommandDefines)475 TEST(ClangdServerTest, ForceReparseCompileCommandDefines) {
476 MockFS FS;
477 ErrorCheckingCallbacks DiagConsumer;
478 MockCompilationDatabase CDB;
479 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
480
481 auto FooCpp = testPath("foo.cpp");
482 const auto SourceContents = R"cpp(
483 #ifdef WITH_ERROR
484 this
485 #endif
486
487 int main() { return 0; }
488 )cpp";
489 FS.Files[FooCpp] = "";
490
491 // Parse with define, we expect to see the errors.
492 CDB.ExtraClangFlags = {"-DWITH_ERROR"};
493 runAddDocument(Server, FooCpp, SourceContents);
494 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
495
496 // Parse without the define, no errors should be produced.
497 CDB.ExtraClangFlags = {};
498 runAddDocument(Server, FooCpp, SourceContents);
499 ASSERT_TRUE(Server.blockUntilIdleForTest());
500 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
501 // Subsequent addDocument call should finish without errors too.
502 runAddDocument(Server, FooCpp, SourceContents);
503 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
504 }
505
506 // Test ClangdServer.reparseOpenedFiles.
TEST(ClangdServerTest,ReparseOpenedFiles)507 TEST(ClangdServerTest, ReparseOpenedFiles) {
508 Annotations FooSource(R"cpp(
509 #ifdef MACRO
510 static void $one[[bob]]() {}
511 #else
512 static void $two[[bob]]() {}
513 #endif
514
515 int main () { bo^b (); return 0; }
516 )cpp");
517
518 Annotations BarSource(R"cpp(
519 #ifdef MACRO
520 this is an error
521 #endif
522 )cpp");
523
524 Annotations BazSource(R"cpp(
525 int hello;
526 )cpp");
527
528 MockFS FS;
529 MockCompilationDatabase CDB;
530 MultipleErrorCheckingCallbacks DiagConsumer;
531 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
532
533 auto FooCpp = testPath("foo.cpp");
534 auto BarCpp = testPath("bar.cpp");
535 auto BazCpp = testPath("baz.cpp");
536
537 FS.Files[FooCpp] = "";
538 FS.Files[BarCpp] = "";
539 FS.Files[BazCpp] = "";
540
541 CDB.ExtraClangFlags = {"-DMACRO=1"};
542 Server.addDocument(FooCpp, FooSource.code());
543 Server.addDocument(BarCpp, BarSource.code());
544 Server.addDocument(BazCpp, BazSource.code());
545 ASSERT_TRUE(Server.blockUntilIdleForTest());
546
547 EXPECT_THAT(DiagConsumer.filesWithDiags(),
548 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, true),
549 Pair(BazCpp, false)));
550
551 auto Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
552 EXPECT_TRUE(bool(Locations));
553 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("one"))));
554
555 // Undefine MACRO, close baz.cpp.
556 CDB.ExtraClangFlags.clear();
557 DiagConsumer.clear();
558 Server.removeDocument(BazCpp);
559 Server.addDocument(FooCpp, FooSource.code());
560 Server.addDocument(BarCpp, BarSource.code());
561 ASSERT_TRUE(Server.blockUntilIdleForTest());
562
563 EXPECT_THAT(DiagConsumer.filesWithDiags(),
564 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, false)));
565
566 Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
567 EXPECT_TRUE(bool(Locations));
568 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("two"))));
569 }
570
571 MATCHER_P4(Stats, Name, UsesMemory, PreambleBuilds, ASTBuilds, "") {
572 return arg.first() == Name &&
573 (arg.second.UsedBytesAST + arg.second.UsedBytesPreamble != 0) ==
574 UsesMemory &&
575 std::tie(arg.second.PreambleBuilds, ASTBuilds) ==
576 std::tie(PreambleBuilds, ASTBuilds);
577 }
578
TEST(ClangdServerTest,FileStats)579 TEST(ClangdServerTest, FileStats) {
580 MockFS FS;
581 ErrorCheckingCallbacks DiagConsumer;
582 MockCompilationDatabase CDB;
583 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
584
585 Path FooCpp = testPath("foo.cpp");
586 const auto SourceContents = R"cpp(
587 struct Something {
588 int method();
589 };
590 )cpp";
591 Path BarCpp = testPath("bar.cpp");
592
593 FS.Files[FooCpp] = "";
594 FS.Files[BarCpp] = "";
595
596 EXPECT_THAT(Server.fileStats(), IsEmpty());
597
598 Server.addDocument(FooCpp, SourceContents);
599 Server.addDocument(BarCpp, SourceContents);
600 ASSERT_TRUE(Server.blockUntilIdleForTest());
601
602 EXPECT_THAT(Server.fileStats(),
603 UnorderedElementsAre(Stats(FooCpp, true, 1, 1),
604 Stats(BarCpp, true, 1, 1)));
605
606 Server.removeDocument(FooCpp);
607 ASSERT_TRUE(Server.blockUntilIdleForTest());
608 EXPECT_THAT(Server.fileStats(), ElementsAre(Stats(BarCpp, true, 1, 1)));
609
610 Server.removeDocument(BarCpp);
611 ASSERT_TRUE(Server.blockUntilIdleForTest());
612 EXPECT_THAT(Server.fileStats(), IsEmpty());
613 }
614
TEST(ClangdServerTest,InvalidCompileCommand)615 TEST(ClangdServerTest, InvalidCompileCommand) {
616 MockFS FS;
617 ErrorCheckingCallbacks DiagConsumer;
618 MockCompilationDatabase CDB;
619
620 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
621
622 auto FooCpp = testPath("foo.cpp");
623 // clang cannot create CompilerInvocation if we pass two files in the
624 // CompileCommand. We pass the file in ExtraFlags once and CDB adds another
625 // one in getCompileCommand().
626 CDB.ExtraClangFlags.push_back(FooCpp);
627
628 // Clang can't parse command args in that case, but we shouldn't crash.
629 runAddDocument(Server, FooCpp, "int main() {}");
630
631 EXPECT_EQ(dumpAST(Server, FooCpp), "<no-ast>");
632 EXPECT_ERROR(runLocateSymbolAt(Server, FooCpp, Position()));
633 EXPECT_ERROR(runFindDocumentHighlights(Server, FooCpp, Position()));
634 EXPECT_ERROR(runRename(Server, FooCpp, Position(), "new_name",
635 clangd::RenameOptions()));
636 EXPECT_ERROR(runSignatureHelp(Server, FooCpp, Position()));
637 // Identifier-based fallback completion.
638 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Position(),
639 clangd::CodeCompleteOptions()))
640 .Completions,
641 ElementsAre(Field(&CodeCompletion::Name, "int"),
642 Field(&CodeCompletion::Name, "main")));
643 }
644
TEST(ClangdThreadingTest,StressTest)645 TEST(ClangdThreadingTest, StressTest) {
646 // Without 'static' clang gives an error for a usage inside TestDiagConsumer.
647 static const unsigned FilesCount = 5;
648 const unsigned RequestsCount = 500;
649 // Blocking requests wait for the parsing to complete, they slow down the test
650 // dramatically, so they are issued rarely. Each
651 // BlockingRequestInterval-request will be a blocking one.
652 const unsigned BlockingRequestInterval = 40;
653
654 const auto SourceContentsWithoutErrors = R"cpp(
655 int a;
656 int b;
657 int c;
658 int d;
659 )cpp";
660
661 const auto SourceContentsWithErrors = R"cpp(
662 int a = x;
663 int b;
664 int c;
665 int d;
666 )cpp";
667
668 // Giving invalid line and column number should not crash ClangdServer, but
669 // just to make sure we're sometimes hitting the bounds inside the file we
670 // limit the intervals of line and column number that are generated.
671 unsigned MaxLineForFileRequests = 7;
672 unsigned MaxColumnForFileRequests = 10;
673
674 std::vector<std::string> FilePaths;
675 MockFS FS;
676 for (unsigned I = 0; I < FilesCount; ++I) {
677 std::string Name = std::string("Foo") + std::to_string(I) + ".cpp";
678 FS.Files[Name] = "";
679 FilePaths.push_back(testPath(Name));
680 }
681
682 struct FileStat {
683 unsigned HitsWithoutErrors = 0;
684 unsigned HitsWithErrors = 0;
685 bool HadErrorsInLastDiags = false;
686 };
687
688 class TestDiagConsumer : public ClangdServer::Callbacks {
689 public:
690 TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
691
692 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
693 std::vector<Diag> Diagnostics) override {
694 StringRef FileIndexStr = llvm::sys::path::stem(File);
695 ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
696
697 unsigned long FileIndex = std::stoul(FileIndexStr.str());
698
699 bool HadError = diagsContainErrors(Diagnostics);
700
701 std::lock_guard<std::mutex> Lock(Mutex);
702 if (HadError)
703 Stats[FileIndex].HitsWithErrors++;
704 else
705 Stats[FileIndex].HitsWithoutErrors++;
706 Stats[FileIndex].HadErrorsInLastDiags = HadError;
707 }
708
709 std::vector<FileStat> takeFileStats() {
710 std::lock_guard<std::mutex> Lock(Mutex);
711 return std::move(Stats);
712 }
713
714 private:
715 std::mutex Mutex;
716 std::vector<FileStat> Stats;
717 };
718
719 struct RequestStats {
720 unsigned RequestsWithoutErrors = 0;
721 unsigned RequestsWithErrors = 0;
722 bool LastContentsHadErrors = false;
723 bool FileIsRemoved = true;
724 };
725
726 std::vector<RequestStats> ReqStats;
727 ReqStats.reserve(FilesCount);
728 for (unsigned FileIndex = 0; FileIndex < FilesCount; ++FileIndex)
729 ReqStats.emplace_back();
730
731 TestDiagConsumer DiagConsumer;
732 {
733 MockCompilationDatabase CDB;
734 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
735
736 // Prepare some random distributions for the test.
737 std::random_device RandGen;
738
739 std::uniform_int_distribution<unsigned> FileIndexDist(0, FilesCount - 1);
740 // Pass a text that contains compiler errors to addDocument in about 20% of
741 // all requests.
742 std::bernoulli_distribution ShouldHaveErrorsDist(0.2);
743 // Line and Column numbers for requests that need them.
744 std::uniform_int_distribution<int> LineDist(0, MaxLineForFileRequests);
745 std::uniform_int_distribution<int> ColumnDist(0, MaxColumnForFileRequests);
746
747 // Some helpers.
748 auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors) {
749 auto &Stats = ReqStats[FileIndex];
750
751 if (HadErrors)
752 ++Stats.RequestsWithErrors;
753 else
754 ++Stats.RequestsWithoutErrors;
755 Stats.LastContentsHadErrors = HadErrors;
756 Stats.FileIsRemoved = false;
757 };
758
759 auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex) {
760 auto &Stats = ReqStats[FileIndex];
761
762 Stats.FileIsRemoved = true;
763 };
764
765 auto AddDocument = [&](unsigned FileIndex, bool SkipCache) {
766 bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen);
767 Server.addDocument(FilePaths[FileIndex],
768 ShouldHaveErrors ? SourceContentsWithErrors
769 : SourceContentsWithoutErrors);
770 UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors);
771 };
772
773 // Various requests that we would randomly run.
774 auto AddDocumentRequest = [&]() {
775 unsigned FileIndex = FileIndexDist(RandGen);
776 AddDocument(FileIndex, /*SkipCache=*/false);
777 };
778
779 auto ForceReparseRequest = [&]() {
780 unsigned FileIndex = FileIndexDist(RandGen);
781 AddDocument(FileIndex, /*SkipCache=*/true);
782 };
783
784 auto RemoveDocumentRequest = [&]() {
785 unsigned FileIndex = FileIndexDist(RandGen);
786 // Make sure we don't violate the ClangdServer's contract.
787 if (ReqStats[FileIndex].FileIsRemoved)
788 AddDocument(FileIndex, /*SkipCache=*/false);
789
790 Server.removeDocument(FilePaths[FileIndex]);
791 UpdateStatsOnRemoveDocument(FileIndex);
792 };
793
794 auto CodeCompletionRequest = [&]() {
795 unsigned FileIndex = FileIndexDist(RandGen);
796 // Make sure we don't violate the ClangdServer's contract.
797 if (ReqStats[FileIndex].FileIsRemoved)
798 AddDocument(FileIndex, /*SkipCache=*/false);
799
800 Position Pos;
801 Pos.line = LineDist(RandGen);
802 Pos.character = ColumnDist(RandGen);
803 // FIXME(ibiryukov): Also test async completion requests.
804 // Simply putting CodeCompletion into async requests now would make
805 // tests slow, since there's no way to cancel previous completion
806 // requests as opposed to AddDocument/RemoveDocument, which are implicitly
807 // cancelled by any subsequent AddDocument/RemoveDocument request to the
808 // same file.
809 cantFail(runCodeComplete(Server, FilePaths[FileIndex], Pos,
810 clangd::CodeCompleteOptions()));
811 };
812
813 auto LocateSymbolRequest = [&]() {
814 unsigned FileIndex = FileIndexDist(RandGen);
815 // Make sure we don't violate the ClangdServer's contract.
816 if (ReqStats[FileIndex].FileIsRemoved)
817 AddDocument(FileIndex, /*SkipCache=*/false);
818
819 Position Pos;
820 Pos.line = LineDist(RandGen);
821 Pos.character = ColumnDist(RandGen);
822
823 ASSERT_TRUE(!!runLocateSymbolAt(Server, FilePaths[FileIndex], Pos));
824 };
825
826 std::vector<std::function<void()>> AsyncRequests = {
827 AddDocumentRequest, ForceReparseRequest, RemoveDocumentRequest};
828 std::vector<std::function<void()>> BlockingRequests = {
829 CodeCompletionRequest, LocateSymbolRequest};
830
831 // Bash requests to ClangdServer in a loop.
832 std::uniform_int_distribution<int> AsyncRequestIndexDist(
833 0, AsyncRequests.size() - 1);
834 std::uniform_int_distribution<int> BlockingRequestIndexDist(
835 0, BlockingRequests.size() - 1);
836 for (unsigned I = 1; I <= RequestsCount; ++I) {
837 if (I % BlockingRequestInterval != 0) {
838 // Issue an async request most of the time. It should be fast.
839 unsigned RequestIndex = AsyncRequestIndexDist(RandGen);
840 AsyncRequests[RequestIndex]();
841 } else {
842 // Issue a blocking request once in a while.
843 auto RequestIndex = BlockingRequestIndexDist(RandGen);
844 BlockingRequests[RequestIndex]();
845 }
846 }
847 ASSERT_TRUE(Server.blockUntilIdleForTest());
848 }
849
850 // Check some invariants about the state of the program.
851 std::vector<FileStat> Stats = DiagConsumer.takeFileStats();
852 for (unsigned I = 0; I < FilesCount; ++I) {
853 if (!ReqStats[I].FileIsRemoved) {
854 ASSERT_EQ(Stats[I].HadErrorsInLastDiags,
855 ReqStats[I].LastContentsHadErrors);
856 }
857
858 ASSERT_LE(Stats[I].HitsWithErrors, ReqStats[I].RequestsWithErrors);
859 ASSERT_LE(Stats[I].HitsWithoutErrors, ReqStats[I].RequestsWithoutErrors);
860 }
861 }
862
TEST(ClangdThreadingTest,NoConcurrentDiagnostics)863 TEST(ClangdThreadingTest, NoConcurrentDiagnostics) {
864 class NoConcurrentAccessDiagConsumer : public ClangdServer::Callbacks {
865 public:
866 std::atomic<int> Count = {0};
867
868 NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
869 : StartSecondReparse(std::move(StartSecondReparse)) {}
870
871 void onDiagnosticsReady(PathRef, llvm::StringRef,
872 std::vector<Diag>) override {
873 ++Count;
874 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
875 ASSERT_TRUE(Lock.owns_lock())
876 << "Detected concurrent onDiagnosticsReady calls for the same file.";
877
878 // If we started the second parse immediately, it might cancel the first.
879 // So we don't allow it to start until the first has delivered diags...
880 if (FirstRequest) {
881 FirstRequest = false;
882 StartSecondReparse.set_value();
883 // ... but then we wait long enough that the callbacks would overlap.
884 std::this_thread::sleep_for(std::chrono::milliseconds(50));
885 }
886 }
887
888 private:
889 std::mutex Mutex;
890 bool FirstRequest = true;
891 std::promise<void> StartSecondReparse;
892 };
893
894 const auto SourceContentsWithoutErrors = R"cpp(
895 int a;
896 int b;
897 int c;
898 int d;
899 )cpp";
900
901 const auto SourceContentsWithErrors = R"cpp(
902 int a = x;
903 int b;
904 int c;
905 int d;
906 )cpp";
907
908 auto FooCpp = testPath("foo.cpp");
909 MockFS FS;
910 FS.Files[FooCpp] = "";
911
912 std::promise<void> StartSecondPromise;
913 std::future<void> StartSecond = StartSecondPromise.get_future();
914
915 NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise));
916 MockCompilationDatabase CDB;
917 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
918 Server.addDocument(FooCpp, SourceContentsWithErrors);
919 StartSecond.wait();
920 Server.addDocument(FooCpp, SourceContentsWithoutErrors);
921 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
922 ASSERT_EQ(DiagConsumer.Count, 2); // Sanity check - we actually ran both?
923 }
924
TEST(ClangdServerTest,FormatCode)925 TEST(ClangdServerTest, FormatCode) {
926 MockFS FS;
927 ErrorCheckingCallbacks DiagConsumer;
928 MockCompilationDatabase CDB;
929 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
930
931 auto Path = testPath("foo.cpp");
932 std::string Code = R"cpp(
933 #include "x.h"
934 #include "y.h"
935
936 void f( ) {}
937 )cpp";
938 std::string Expected = R"cpp(
939 #include "x.h"
940 #include "y.h"
941
942 void f() {}
943 )cpp";
944 FS.Files[Path] = Code;
945 runAddDocument(Server, Path, Code);
946
947 auto Replaces = runFormatFile(Server, Path, Code);
948 EXPECT_TRUE(static_cast<bool>(Replaces));
949 auto Changed = tooling::applyAllReplacements(Code, *Replaces);
950 EXPECT_TRUE(static_cast<bool>(Changed));
951 EXPECT_EQ(Expected, *Changed);
952 }
953
TEST(ClangdServerTest,ChangedHeaderFromISystem)954 TEST(ClangdServerTest, ChangedHeaderFromISystem) {
955 MockFS FS;
956 ErrorCheckingCallbacks DiagConsumer;
957 MockCompilationDatabase CDB;
958 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
959
960 auto SourcePath = testPath("source/foo.cpp");
961 auto HeaderPath = testPath("headers/foo.h");
962 FS.Files[HeaderPath] = "struct X { int bar; };";
963 Annotations Code(R"cpp(
964 #include "foo.h"
965
966 int main() {
967 X().ba^
968 })cpp");
969 CDB.ExtraClangFlags.push_back("-xc++");
970 CDB.ExtraClangFlags.push_back("-isystem" + testPath("headers"));
971
972 runAddDocument(Server, SourcePath, Code.code());
973 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
974 clangd::CodeCompleteOptions()))
975 .Completions;
976 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar")));
977 // Update the header and rerun addDocument to make sure we get the updated
978 // files.
979 FS.Files[HeaderPath] = "struct X { int bar; int baz; };";
980 runAddDocument(Server, SourcePath, Code.code());
981 Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
982 clangd::CodeCompleteOptions()))
983 .Completions;
984 // We want to make sure we see the updated version.
985 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar"),
986 Field(&CodeCompletion::Name, "baz")));
987 }
988
989 // FIXME(ioeric): make this work for windows again.
990 #ifndef _WIN32
991 // Check that running code completion doesn't stat() a bunch of files from the
992 // preamble again. (They should be using the preamble's stat-cache)
TEST(ClangdTests,PreambleVFSStatCache)993 TEST(ClangdTests, PreambleVFSStatCache) {
994 class StatRecordingFS : public ThreadsafeFS {
995 llvm::StringMap<unsigned> &CountStats;
996
997 public:
998 // If relative paths are used, they are resolved with testPath().
999 llvm::StringMap<std::string> Files;
1000
1001 StatRecordingFS(llvm::StringMap<unsigned> &CountStats)
1002 : CountStats(CountStats) {}
1003
1004 private:
1005 IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override {
1006 class StatRecordingVFS : public llvm::vfs::ProxyFileSystem {
1007 public:
1008 StatRecordingVFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
1009 llvm::StringMap<unsigned> &CountStats)
1010 : ProxyFileSystem(std::move(FS)), CountStats(CountStats) {}
1011
1012 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
1013 openFileForRead(const Twine &Path) override {
1014 ++CountStats[llvm::sys::path::filename(Path.str())];
1015 return ProxyFileSystem::openFileForRead(Path);
1016 }
1017 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
1018 ++CountStats[llvm::sys::path::filename(Path.str())];
1019 return ProxyFileSystem::status(Path);
1020 }
1021
1022 private:
1023 llvm::StringMap<unsigned> &CountStats;
1024 };
1025
1026 return IntrusiveRefCntPtr<StatRecordingVFS>(
1027 new StatRecordingVFS(buildTestFS(Files), CountStats));
1028 }
1029 };
1030
1031 llvm::StringMap<unsigned> CountStats;
1032 StatRecordingFS FS(CountStats);
1033 ErrorCheckingCallbacks DiagConsumer;
1034 MockCompilationDatabase CDB;
1035 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1036
1037 auto SourcePath = testPath("foo.cpp");
1038 auto HeaderPath = testPath("foo.h");
1039 FS.Files[HeaderPath] = "struct TestSym {};";
1040 Annotations Code(R"cpp(
1041 #include "foo.h"
1042
1043 int main() {
1044 TestSy^
1045 })cpp");
1046
1047 runAddDocument(Server, SourcePath, Code.code());
1048
1049 unsigned Before = CountStats["foo.h"];
1050 EXPECT_GT(Before, 0u);
1051 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
1052 clangd::CodeCompleteOptions()))
1053 .Completions;
1054 EXPECT_EQ(CountStats["foo.h"], Before);
1055 EXPECT_THAT(Completions,
1056 ElementsAre(Field(&CodeCompletion::Name, "TestSym")));
1057 }
1058 #endif
1059
TEST(ClangdServerTest,FallbackWhenPreambleIsNotReady)1060 TEST(ClangdServerTest, FallbackWhenPreambleIsNotReady) {
1061 MockFS FS;
1062 ErrorCheckingCallbacks DiagConsumer;
1063 MockCompilationDatabase CDB;
1064 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1065
1066 auto FooCpp = testPath("foo.cpp");
1067 Annotations Code(R"cpp(
1068 namespace ns { int xyz; }
1069 using namespace ns;
1070 int main() {
1071 xy^
1072 })cpp");
1073 FS.Files[FooCpp] = FooCpp;
1074
1075 auto Opts = clangd::CodeCompleteOptions();
1076 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1077
1078 // This will make compile command broken and preamble absent.
1079 CDB.ExtraClangFlags = {"yolo.cc"};
1080 Server.addDocument(FooCpp, Code.code());
1081 ASSERT_TRUE(Server.blockUntilIdleForTest());
1082 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1083 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1084 // Identifier-based fallback completion doesn't know about "symbol" scope.
1085 EXPECT_THAT(Res.Completions,
1086 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1087 Field(&CodeCompletion::Scope, ""))));
1088
1089 // Make the compile command work again.
1090 CDB.ExtraClangFlags = {"-std=c++11"};
1091 Server.addDocument(FooCpp, Code.code());
1092 ASSERT_TRUE(Server.blockUntilIdleForTest());
1093 EXPECT_THAT(
1094 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1095 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1096 Field(&CodeCompletion::Scope, "ns::"))));
1097
1098 // Now force identifier-based completion.
1099 Opts.RunParser = CodeCompleteOptions::NeverParse;
1100 EXPECT_THAT(
1101 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1102 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1103 Field(&CodeCompletion::Scope, ""))));
1104 }
1105
TEST(ClangdServerTest,FallbackWhenWaitingForCompileCommand)1106 TEST(ClangdServerTest, FallbackWhenWaitingForCompileCommand) {
1107 MockFS FS;
1108 ErrorCheckingCallbacks DiagConsumer;
1109 // Returns compile command only when notified.
1110 class DelayedCompilationDatabase : public GlobalCompilationDatabase {
1111 public:
1112 DelayedCompilationDatabase(Notification &CanReturnCommand)
1113 : CanReturnCommand(CanReturnCommand) {}
1114
1115 llvm::Optional<tooling::CompileCommand>
1116 getCompileCommand(PathRef File) const override {
1117 // FIXME: make this timeout and fail instead of waiting forever in case
1118 // something goes wrong.
1119 CanReturnCommand.wait();
1120 auto FileName = llvm::sys::path::filename(File);
1121 std::vector<std::string> CommandLine = {"clangd", "-ffreestanding",
1122 std::string(File)};
1123 return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
1124 FileName, std::move(CommandLine), "")};
1125 }
1126
1127 std::vector<std::string> ExtraClangFlags;
1128
1129 private:
1130 Notification &CanReturnCommand;
1131 };
1132
1133 Notification CanReturnCommand;
1134 DelayedCompilationDatabase CDB(CanReturnCommand);
1135 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1136
1137 auto FooCpp = testPath("foo.cpp");
1138 Annotations Code(R"cpp(
1139 namespace ns { int xyz; }
1140 using namespace ns;
1141 int main() {
1142 xy^
1143 })cpp");
1144 FS.Files[FooCpp] = FooCpp;
1145 Server.addDocument(FooCpp, Code.code());
1146
1147 // Sleep for some time to make sure code completion is not run because update
1148 // hasn't been scheduled.
1149 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1150 auto Opts = clangd::CodeCompleteOptions();
1151 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1152
1153 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1154 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1155
1156 CanReturnCommand.notify();
1157 ASSERT_TRUE(Server.blockUntilIdleForTest());
1158 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
1159 clangd::CodeCompleteOptions()))
1160 .Completions,
1161 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1162 Field(&CodeCompletion::Scope, "ns::"))));
1163 }
1164
TEST(ClangdServerTest,CustomAction)1165 TEST(ClangdServerTest, CustomAction) {
1166 OverlayCDB CDB(/*Base=*/nullptr);
1167 MockFS FS;
1168 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
1169
1170 Server.addDocument(testPath("foo.cc"), "void x();");
1171 Decl::Kind XKind = Decl::TranslationUnit;
1172 EXPECT_THAT_ERROR(runCustomAction(Server, testPath("foo.cc"),
1173 [&](InputsAndAST AST) {
1174 XKind = findDecl(AST.AST, "x").getKind();
1175 }),
1176 llvm::Succeeded());
1177 EXPECT_EQ(XKind, Decl::Function);
1178 }
1179
1180 // Tests fails when built with asan due to stack overflow. So skip running the
1181 // test as a workaround.
1182 #if !defined(__has_feature) || !__has_feature(address_sanitizer)
TEST(ClangdServerTest,TestStackOverflow)1183 TEST(ClangdServerTest, TestStackOverflow) {
1184 MockFS FS;
1185 ErrorCheckingCallbacks DiagConsumer;
1186 MockCompilationDatabase CDB;
1187 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1188
1189 const char *SourceContents = R"cpp(
1190 constexpr int foo() { return foo(); }
1191 static_assert(foo());
1192 )cpp";
1193
1194 auto FooCpp = testPath("foo.cpp");
1195 FS.Files[FooCpp] = SourceContents;
1196
1197 Server.addDocument(FooCpp, SourceContents);
1198 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
1199 // check that we got a constexpr depth error, and not crashed by stack
1200 // overflow
1201 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
1202 }
1203 #endif
1204
TEST(ClangdServer,TidyOverrideTest)1205 TEST(ClangdServer, TidyOverrideTest) {
1206 struct DiagsCheckingCallback : public ClangdServer::Callbacks {
1207 public:
1208 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
1209 std::vector<Diag> Diagnostics) override {
1210 std::lock_guard<std::mutex> Lock(Mutex);
1211 HadDiagsInLastCallback = !Diagnostics.empty();
1212 }
1213
1214 std::mutex Mutex;
1215 bool HadDiagsInLastCallback = false;
1216 } DiagConsumer;
1217
1218 MockFS FS;
1219 // These checks don't work well in clangd, even if configured they shouldn't
1220 // run.
1221 FS.Files[testPath(".clang-tidy")] = R"(
1222 Checks: -*,bugprone-use-after-move,llvm-header-guard
1223 )";
1224 MockCompilationDatabase CDB;
1225 std::vector<TidyProvider> Stack;
1226 Stack.push_back(provideClangTidyFiles(FS));
1227 Stack.push_back(disableUnusableChecks());
1228 TidyProvider Provider = combine(std::move(Stack));
1229 CDB.ExtraClangFlags = {"-xc++"};
1230 auto Opts = ClangdServer::optsForTest();
1231 Opts.ClangTidyProvider = Provider;
1232 ClangdServer Server(CDB, FS, Opts, &DiagConsumer);
1233 const char *SourceContents = R"cpp(
1234 struct Foo { Foo(); Foo(Foo&); Foo(Foo&&); };
1235 namespace std { Foo&& move(Foo&); }
1236 void foo() {
1237 Foo x;
1238 Foo y = std::move(x);
1239 Foo z = x;
1240 })cpp";
1241 Server.addDocument(testPath("foo.h"), SourceContents);
1242 ASSERT_TRUE(Server.blockUntilIdleForTest());
1243 EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback);
1244 }
1245
TEST(ClangdServer,MemoryUsageTest)1246 TEST(ClangdServer, MemoryUsageTest) {
1247 MockFS FS;
1248 MockCompilationDatabase CDB;
1249 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
1250
1251 auto FooCpp = testPath("foo.cpp");
1252 Server.addDocument(FooCpp, "");
1253 ASSERT_TRUE(Server.blockUntilIdleForTest());
1254
1255 llvm::BumpPtrAllocator Alloc;
1256 MemoryTree MT(&Alloc);
1257 Server.profile(MT);
1258 ASSERT_TRUE(MT.children().count("tuscheduler"));
1259 EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp));
1260 }
1261 } // namespace
1262 } // namespace clangd
1263 } // namespace clang
1264