Bump version to 19.1.0git
[llvm-project.git] / clang-tools-extra / clangd / unittests / ClangdTests.cpp
blobc324643498d94cbb7c94da0521052e3f883515ee
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 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
10 #include "ClangdServer.h"
11 #include "CodeComplete.h"
12 #include "CompileCommands.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 "refactor/Tweak.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 "clang/Tooling/Core/Replacement.h"
28 #include "llvm/ADT/ArrayRef.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/Error.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 <optional>
44 #include <random>
45 #include <string>
46 #include <thread>
47 #include <vector>
49 namespace clang {
50 namespace clangd {
52 namespace {
54 using ::testing::AllOf;
55 using ::testing::ElementsAre;
56 using ::testing::Field;
57 using ::testing::IsEmpty;
58 using ::testing::Pair;
59 using ::testing::SizeIs;
60 using ::testing::UnorderedElementsAre;
62 MATCHER_P2(DeclAt, File, Range, "") {
63 return arg.PreferredDeclaration ==
64 Location{URIForFile::canonicalize(File, testRoot()), Range};
67 bool diagsContainErrors(const std::vector<Diag> &Diagnostics) {
68 for (auto D : Diagnostics) {
69 if (D.Severity == DiagnosticsEngine::Error ||
70 D.Severity == DiagnosticsEngine::Fatal)
71 return true;
73 return false;
76 class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
77 public:
78 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
79 llvm::ArrayRef<Diag> Diagnostics) override {
80 bool HadError = diagsContainErrors(Diagnostics);
81 std::lock_guard<std::mutex> Lock(Mutex);
82 HadErrorInLastDiags = HadError;
85 bool hadErrorInLastDiags() {
86 std::lock_guard<std::mutex> Lock(Mutex);
87 return HadErrorInLastDiags;
90 private:
91 std::mutex Mutex;
92 bool HadErrorInLastDiags = false;
95 /// For each file, record whether the last published diagnostics contained at
96 /// least one error.
97 class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
98 public:
99 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
100 llvm::ArrayRef<Diag> Diagnostics) override {
101 bool HadError = diagsContainErrors(Diagnostics);
103 std::lock_guard<std::mutex> Lock(Mutex);
104 LastDiagsHadError[File] = HadError;
107 /// Exposes all files consumed by onDiagnosticsReady in an unspecified order.
108 /// For each file, a bool value indicates whether the last diagnostics
109 /// contained an error.
110 std::vector<std::pair<Path, bool>> filesWithDiags() const {
111 std::vector<std::pair<Path, bool>> Result;
112 std::lock_guard<std::mutex> Lock(Mutex);
113 for (const auto &It : LastDiagsHadError)
114 Result.emplace_back(std::string(It.first()), It.second);
115 return Result;
118 void clear() {
119 std::lock_guard<std::mutex> Lock(Mutex);
120 LastDiagsHadError.clear();
123 private:
124 mutable std::mutex Mutex;
125 llvm::StringMap<bool> LastDiagsHadError;
128 /// Replaces all patterns of the form 0x123abc with spaces
129 std::string replacePtrsInDump(std::string const &Dump) {
130 llvm::Regex RE("0x[0-9a-fA-F]+");
131 llvm::SmallVector<llvm::StringRef, 1> Matches;
132 llvm::StringRef Pending = Dump;
134 std::string Result;
135 while (RE.match(Pending, &Matches)) {
136 assert(Matches.size() == 1 && "Exactly one match expected");
137 auto MatchPos = Matches[0].data() - Pending.data();
139 Result += Pending.take_front(MatchPos);
140 Pending = Pending.drop_front(MatchPos + Matches[0].size());
142 Result += Pending;
144 return Result;
147 std::string dumpAST(ClangdServer &Server, PathRef File) {
148 std::string Result;
149 Notification Done;
150 Server.customAction(File, "DumpAST", [&](llvm::Expected<InputsAndAST> AST) {
151 if (AST) {
152 llvm::raw_string_ostream ResultOS(Result);
153 AST->AST.getASTContext().getTranslationUnitDecl()->dump(ResultOS, true);
154 } else {
155 llvm::consumeError(AST.takeError());
156 Result = "<no-ast>";
158 Done.notify();
160 Done.wait();
161 return Result;
164 std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
165 return replacePtrsInDump(dumpAST(Server, File));
168 std::string parseSourceAndDumpAST(
169 PathRef SourceFileRelPath, llvm::StringRef SourceContents,
170 std::vector<std::pair<PathRef, llvm::StringRef>> ExtraFiles = {},
171 bool ExpectErrors = false) {
172 MockFS FS;
173 ErrorCheckingCallbacks DiagConsumer;
174 MockCompilationDatabase CDB;
175 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
176 for (const auto &FileWithContents : ExtraFiles)
177 FS.Files[testPath(FileWithContents.first)] =
178 std::string(FileWithContents.second);
180 auto SourceFilename = testPath(SourceFileRelPath);
181 Server.addDocument(SourceFilename, SourceContents);
182 auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename);
183 EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
184 EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
185 return Result;
188 TEST(ClangdServerTest, Parse) {
189 // FIXME: figure out a stable format for AST dumps, so that we can check the
190 // output of the dump itself is equal to the expected one, not just that it's
191 // different.
192 auto Empty = parseSourceAndDumpAST("foo.cpp", "");
193 auto OneDecl = parseSourceAndDumpAST("foo.cpp", "int a;");
194 auto SomeDecls = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
195 EXPECT_NE(Empty, OneDecl);
196 EXPECT_NE(Empty, SomeDecls);
197 EXPECT_NE(SomeDecls, OneDecl);
199 auto Empty2 = parseSourceAndDumpAST("foo.cpp", "");
200 auto OneDecl2 = parseSourceAndDumpAST("foo.cpp", "int a;");
201 auto SomeDecls2 = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
202 EXPECT_EQ(Empty, Empty2);
203 EXPECT_EQ(OneDecl, OneDecl2);
204 EXPECT_EQ(SomeDecls, SomeDecls2);
207 TEST(ClangdServerTest, ParseWithHeader) {
208 parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {},
209 /*ExpectErrors=*/true);
210 parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {{"foo.h", ""}},
211 /*ExpectErrors=*/false);
213 const auto *SourceContents = R"cpp(
214 #include "foo.h"
215 int b = a;
216 )cpp";
217 parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", ""}},
218 /*ExpectErrors=*/true);
219 parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", "int a;"}},
220 /*ExpectErrors=*/false);
223 TEST(ClangdServerTest, Reparse) {
224 MockFS FS;
225 ErrorCheckingCallbacks DiagConsumer;
226 MockCompilationDatabase CDB;
227 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
229 const auto *SourceContents = R"cpp(
230 #include "foo.h"
231 int b = a;
232 )cpp";
234 auto FooCpp = testPath("foo.cpp");
236 FS.Files[testPath("foo.h")] = "int a;";
237 FS.Files[FooCpp] = SourceContents;
239 Server.addDocument(FooCpp, SourceContents);
240 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
241 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
242 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
244 Server.addDocument(FooCpp, "");
245 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
246 auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
247 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
249 Server.addDocument(FooCpp, SourceContents);
250 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
251 auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
252 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
254 EXPECT_EQ(DumpParse1, DumpParse2);
255 EXPECT_NE(DumpParse1, DumpParseEmpty);
258 TEST(ClangdServerTest, ReparseOnHeaderChange) {
259 MockFS FS;
260 ErrorCheckingCallbacks DiagConsumer;
261 MockCompilationDatabase CDB;
262 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
264 const auto *SourceContents = R"cpp(
265 #include "foo.h"
266 int b = a;
267 )cpp";
269 auto FooCpp = testPath("foo.cpp");
270 auto FooH = testPath("foo.h");
272 FS.Files[FooH] = "int a;";
273 FS.Files[FooCpp] = SourceContents;
275 Server.addDocument(FooCpp, SourceContents);
276 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
277 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
278 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
280 FS.Files[FooH] = "";
281 Server.addDocument(FooCpp, SourceContents);
282 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
283 auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp);
284 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
286 FS.Files[FooH] = "int a;";
287 Server.addDocument(FooCpp, SourceContents);
288 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
289 auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
290 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
292 EXPECT_EQ(DumpParse1, DumpParse2);
293 EXPECT_NE(DumpParse1, DumpParseDifferent);
296 TEST(ClangdServerTest, PropagatesContexts) {
297 static Key<int> Secret;
298 struct ContextReadingFS : public ThreadsafeFS {
299 mutable int Got;
301 private:
302 IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override {
303 Got = Context::current().getExisting(Secret);
304 return buildTestFS({});
306 } FS;
307 struct Callbacks : public ClangdServer::Callbacks {
308 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
309 llvm::ArrayRef<Diag> Diagnostics) override {
310 Got = Context::current().getExisting(Secret);
312 int Got;
313 } Callbacks;
314 MockCompilationDatabase CDB;
316 // Verify that the context is plumbed to the FS provider and diagnostics.
317 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &Callbacks);
319 WithContextValue Entrypoint(Secret, 42);
320 Server.addDocument(testPath("foo.cpp"), "void main(){}");
322 ASSERT_TRUE(Server.blockUntilIdleForTest());
323 EXPECT_EQ(FS.Got, 42);
324 EXPECT_EQ(Callbacks.Got, 42);
327 TEST(ClangdServerTest, RespectsConfig) {
328 // Go-to-definition will resolve as marked if FOO is defined.
329 Annotations Example(R"cpp(
330 #ifdef FOO
331 int [[x]];
332 #else
333 int x;
334 #endif
335 int y = ^x;
336 )cpp");
337 // Provide conditional config that defines FOO for foo.cc.
338 class ConfigProvider : public config::Provider {
339 std::vector<config::CompiledFragment>
340 getFragments(const config::Params &,
341 config::DiagnosticCallback DC) const override {
342 config::Fragment F;
343 F.If.PathMatch.emplace_back(".*foo.cc");
344 F.CompileFlags.Add.emplace_back("-DFOO=1");
345 return {std::move(F).compile(DC)};
347 } CfgProvider;
349 auto Opts = ClangdServer::optsForTest();
350 Opts.ContextProvider =
351 ClangdServer::createConfiguredContextProvider(&CfgProvider, nullptr);
352 OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{},
353 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());
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 llvm::ArrayRef<Diag> Diagnostics) override {
376 Got = Version.str();
378 std::string Got = "";
379 } Callbacks;
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");
387 // Only enable this test on Unix
388 #ifdef LLVM_ON_UNIX
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-unknown-linux-gnu",
396 "-m64", "--gcc-toolchain=/randomusr",
397 "-stdlib=libstdc++"});
398 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
400 // Just a random gcc version string
401 SmallString<8> Version("4.9.3");
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);
407 // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
408 // installation there.
409 SmallString<64> MockLibFile;
410 llvm::sys::path::append(MockLibFile, LibDir, "64", "crtbegin.o");
411 FS.Files[MockLibFile] = "";
413 SmallString<64> IncludeDir("/randomusr/include/c++");
414 llvm::sys::path::append(IncludeDir, Version);
416 SmallString<64> StringPath;
417 llvm::sys::path::append(StringPath, IncludeDir, "string");
418 FS.Files[StringPath] = "class mock_string {};";
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;
427 runAddDocument(Server, FooCpp, SourceContents);
428 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
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());
437 #endif // LLVM_ON_UNIX
439 TEST(ClangdServerTest, ForceReparseCompileCommand) {
440 MockFS FS;
441 ErrorCheckingCallbacks DiagConsumer;
442 MockCompilationDatabase CDB;
443 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
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";
455 FS.Files[FooCpp] = "";
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());
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());
475 TEST(ClangdServerTest, ForceReparseCompileCommandDefines) {
476 MockFS FS;
477 ErrorCheckingCallbacks DiagConsumer;
478 MockCompilationDatabase CDB;
479 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
481 auto FooCpp = testPath("foo.cpp");
482 const auto *SourceContents = R"cpp(
483 #ifdef WITH_ERROR
484 this
485 #endif
487 int main() { return 0; }
488 )cpp";
489 FS.Files[FooCpp] = "";
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());
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());
506 // Test ClangdServer.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
515 int main () { bo^b (); return 0; }
516 )cpp");
518 Annotations BarSource(R"cpp(
519 #ifdef MACRO
520 this is an error
521 #endif
522 )cpp");
524 Annotations BazSource(R"cpp(
525 int hello;
526 )cpp");
528 MockFS FS;
529 MockCompilationDatabase CDB;
530 MultipleErrorCheckingCallbacks DiagConsumer;
531 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
533 auto FooCpp = testPath("foo.cpp");
534 auto BarCpp = testPath("bar.cpp");
535 auto BazCpp = testPath("baz.cpp");
537 FS.Files[FooCpp] = "";
538 FS.Files[BarCpp] = "";
539 FS.Files[BazCpp] = "";
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());
547 EXPECT_THAT(DiagConsumer.filesWithDiags(),
548 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, true),
549 Pair(BazCpp, false)));
551 auto Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
552 EXPECT_TRUE(bool(Locations));
553 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("one"))));
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());
563 EXPECT_THAT(DiagConsumer.filesWithDiags(),
564 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, false)));
566 Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
567 EXPECT_TRUE(bool(Locations));
568 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("two"))));
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);
579 TEST(ClangdServerTest, FileStats) {
580 MockFS FS;
581 ErrorCheckingCallbacks DiagConsumer;
582 MockCompilationDatabase CDB;
583 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
585 Path FooCpp = testPath("foo.cpp");
586 const auto *SourceContents = R"cpp(
587 struct Something {
588 int method();
590 )cpp";
591 Path BarCpp = testPath("bar.cpp");
593 FS.Files[FooCpp] = "";
594 FS.Files[BarCpp] = "";
596 EXPECT_THAT(Server.fileStats(), IsEmpty());
598 Server.addDocument(FooCpp, SourceContents);
599 Server.addDocument(BarCpp, SourceContents);
600 ASSERT_TRUE(Server.blockUntilIdleForTest());
602 EXPECT_THAT(Server.fileStats(),
603 UnorderedElementsAre(Stats(FooCpp, true, 1, 1),
604 Stats(BarCpp, true, 1, 1)));
606 Server.removeDocument(FooCpp);
607 ASSERT_TRUE(Server.blockUntilIdleForTest());
608 EXPECT_THAT(Server.fileStats(), ElementsAre(Stats(BarCpp, true, 1, 1)));
610 Server.removeDocument(BarCpp);
611 ASSERT_TRUE(Server.blockUntilIdleForTest());
612 EXPECT_THAT(Server.fileStats(), IsEmpty());
615 TEST(ClangdServerTest, InvalidCompileCommand) {
616 MockFS FS;
617 ErrorCheckingCallbacks DiagConsumer;
618 MockCompilationDatabase CDB;
620 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
622 auto FooCpp = testPath("foo.cpp");
623 // clang cannot create CompilerInvocation in this case.
624 CDB.ExtraClangFlags.push_back("-###");
626 // Clang can't parse command args in that case, but we shouldn't crash.
627 runAddDocument(Server, FooCpp, "int main() {}");
629 EXPECT_EQ(dumpAST(Server, FooCpp), "<no-ast>");
630 EXPECT_ERROR(runLocateSymbolAt(Server, FooCpp, Position()));
631 EXPECT_ERROR(runFindDocumentHighlights(Server, FooCpp, Position()));
632 EXPECT_ERROR(runRename(Server, FooCpp, Position(), "new_name",
633 clangd::RenameOptions()));
634 EXPECT_ERROR(
635 runSignatureHelp(Server, FooCpp, Position(), MarkupKind::PlainText));
636 // Identifier-based fallback completion.
637 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Position(),
638 clangd::CodeCompleteOptions()))
639 .Completions,
640 ElementsAre(Field(&CodeCompletion::Name, "int"),
641 Field(&CodeCompletion::Name, "main")));
644 TEST(ClangdThreadingTest, StressTest) {
645 // Without 'static' clang gives an error for a usage inside TestDiagConsumer.
646 static const unsigned FilesCount = 5;
647 const unsigned RequestsCount = 500;
648 // Blocking requests wait for the parsing to complete, they slow down the test
649 // dramatically, so they are issued rarely. Each
650 // BlockingRequestInterval-request will be a blocking one.
651 const unsigned BlockingRequestInterval = 40;
653 const auto *SourceContentsWithoutErrors = R"cpp(
654 int a;
655 int b;
656 int c;
657 int d;
658 )cpp";
660 const auto *SourceContentsWithErrors = R"cpp(
661 int a = x;
662 int b;
663 int c;
664 int d;
665 )cpp";
667 // Giving invalid line and column number should not crash ClangdServer, but
668 // just to make sure we're sometimes hitting the bounds inside the file we
669 // limit the intervals of line and column number that are generated.
670 unsigned MaxLineForFileRequests = 7;
671 unsigned MaxColumnForFileRequests = 10;
673 std::vector<std::string> FilePaths;
674 MockFS FS;
675 for (unsigned I = 0; I < FilesCount; ++I) {
676 std::string Name = std::string("Foo") + std::to_string(I) + ".cpp";
677 FS.Files[Name] = "";
678 FilePaths.push_back(testPath(Name));
681 struct FileStat {
682 unsigned HitsWithoutErrors = 0;
683 unsigned HitsWithErrors = 0;
684 bool HadErrorsInLastDiags = false;
687 class TestDiagConsumer : public ClangdServer::Callbacks {
688 public:
689 TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
691 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
692 llvm::ArrayRef<Diag> Diagnostics) override {
693 StringRef FileIndexStr = llvm::sys::path::stem(File);
694 ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
696 unsigned long FileIndex = std::stoul(FileIndexStr.str());
698 bool HadError = diagsContainErrors(Diagnostics);
700 std::lock_guard<std::mutex> Lock(Mutex);
701 if (HadError)
702 Stats[FileIndex].HitsWithErrors++;
703 else
704 Stats[FileIndex].HitsWithoutErrors++;
705 Stats[FileIndex].HadErrorsInLastDiags = HadError;
708 std::vector<FileStat> takeFileStats() {
709 std::lock_guard<std::mutex> Lock(Mutex);
710 return std::move(Stats);
713 private:
714 std::mutex Mutex;
715 std::vector<FileStat> Stats;
718 struct RequestStats {
719 unsigned RequestsWithoutErrors = 0;
720 unsigned RequestsWithErrors = 0;
721 bool LastContentsHadErrors = false;
722 bool FileIsRemoved = true;
725 std::vector<RequestStats> ReqStats;
726 ReqStats.reserve(FilesCount);
727 for (unsigned FileIndex = 0; FileIndex < FilesCount; ++FileIndex)
728 ReqStats.emplace_back();
730 TestDiagConsumer DiagConsumer;
732 MockCompilationDatabase CDB;
733 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
735 // Prepare some random distributions for the test.
736 std::random_device RandGen;
738 std::uniform_int_distribution<unsigned> FileIndexDist(0, FilesCount - 1);
739 // Pass a text that contains compiler errors to addDocument in about 20% of
740 // all requests.
741 std::bernoulli_distribution ShouldHaveErrorsDist(0.2);
742 // Line and Column numbers for requests that need them.
743 std::uniform_int_distribution<int> LineDist(0, MaxLineForFileRequests);
744 std::uniform_int_distribution<int> ColumnDist(0, MaxColumnForFileRequests);
746 // Some helpers.
747 auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors) {
748 auto &Stats = ReqStats[FileIndex];
750 if (HadErrors)
751 ++Stats.RequestsWithErrors;
752 else
753 ++Stats.RequestsWithoutErrors;
754 Stats.LastContentsHadErrors = HadErrors;
755 Stats.FileIsRemoved = false;
758 auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex) {
759 auto &Stats = ReqStats[FileIndex];
761 Stats.FileIsRemoved = true;
764 auto AddDocument = [&](unsigned FileIndex, bool SkipCache) {
765 bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen);
766 Server.addDocument(FilePaths[FileIndex],
767 ShouldHaveErrors ? SourceContentsWithErrors
768 : SourceContentsWithoutErrors);
769 UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors);
772 // Various requests that we would randomly run.
773 auto AddDocumentRequest = [&]() {
774 unsigned FileIndex = FileIndexDist(RandGen);
775 AddDocument(FileIndex, /*SkipCache=*/false);
778 auto ForceReparseRequest = [&]() {
779 unsigned FileIndex = FileIndexDist(RandGen);
780 AddDocument(FileIndex, /*SkipCache=*/true);
783 auto RemoveDocumentRequest = [&]() {
784 unsigned FileIndex = FileIndexDist(RandGen);
785 // Make sure we don't violate the ClangdServer's contract.
786 if (ReqStats[FileIndex].FileIsRemoved)
787 AddDocument(FileIndex, /*SkipCache=*/false);
789 Server.removeDocument(FilePaths[FileIndex]);
790 UpdateStatsOnRemoveDocument(FileIndex);
793 auto CodeCompletionRequest = [&]() {
794 unsigned FileIndex = FileIndexDist(RandGen);
795 // Make sure we don't violate the ClangdServer's contract.
796 if (ReqStats[FileIndex].FileIsRemoved)
797 AddDocument(FileIndex, /*SkipCache=*/false);
799 Position Pos;
800 Pos.line = LineDist(RandGen);
801 Pos.character = ColumnDist(RandGen);
802 // FIXME(ibiryukov): Also test async completion requests.
803 // Simply putting CodeCompletion into async requests now would make
804 // tests slow, since there's no way to cancel previous completion
805 // requests as opposed to AddDocument/RemoveDocument, which are implicitly
806 // cancelled by any subsequent AddDocument/RemoveDocument request to the
807 // same file.
808 cantFail(runCodeComplete(Server, FilePaths[FileIndex], Pos,
809 clangd::CodeCompleteOptions()));
812 auto LocateSymbolRequest = [&]() {
813 unsigned FileIndex = FileIndexDist(RandGen);
814 // Make sure we don't violate the ClangdServer's contract.
815 if (ReqStats[FileIndex].FileIsRemoved)
816 AddDocument(FileIndex, /*SkipCache=*/false);
818 Position Pos;
819 Pos.line = LineDist(RandGen);
820 Pos.character = ColumnDist(RandGen);
822 ASSERT_TRUE(!!runLocateSymbolAt(Server, FilePaths[FileIndex], Pos));
825 std::vector<std::function<void()>> AsyncRequests = {
826 AddDocumentRequest, ForceReparseRequest, RemoveDocumentRequest};
827 std::vector<std::function<void()>> BlockingRequests = {
828 CodeCompletionRequest, LocateSymbolRequest};
830 // Bash requests to ClangdServer in a loop.
831 std::uniform_int_distribution<int> AsyncRequestIndexDist(
832 0, AsyncRequests.size() - 1);
833 std::uniform_int_distribution<int> BlockingRequestIndexDist(
834 0, BlockingRequests.size() - 1);
835 for (unsigned I = 1; I <= RequestsCount; ++I) {
836 if (I % BlockingRequestInterval != 0) {
837 // Issue an async request most of the time. It should be fast.
838 unsigned RequestIndex = AsyncRequestIndexDist(RandGen);
839 AsyncRequests[RequestIndex]();
840 } else {
841 // Issue a blocking request once in a while.
842 auto RequestIndex = BlockingRequestIndexDist(RandGen);
843 BlockingRequests[RequestIndex]();
846 ASSERT_TRUE(Server.blockUntilIdleForTest());
849 // Check some invariants about the state of the program.
850 std::vector<FileStat> Stats = DiagConsumer.takeFileStats();
851 for (unsigned I = 0; I < FilesCount; ++I) {
852 if (!ReqStats[I].FileIsRemoved) {
853 ASSERT_EQ(Stats[I].HadErrorsInLastDiags,
854 ReqStats[I].LastContentsHadErrors);
857 ASSERT_LE(Stats[I].HitsWithErrors, ReqStats[I].RequestsWithErrors);
858 ASSERT_LE(Stats[I].HitsWithoutErrors, ReqStats[I].RequestsWithoutErrors);
862 TEST(ClangdThreadingTest, NoConcurrentDiagnostics) {
863 class NoConcurrentAccessDiagConsumer : public ClangdServer::Callbacks {
864 public:
865 std::atomic<int> Count = {0};
867 NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
868 : StartSecondReparse(std::move(StartSecondReparse)) {}
870 void onDiagnosticsReady(PathRef, llvm::StringRef,
871 llvm::ArrayRef<Diag>) override {
872 ++Count;
873 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
874 ASSERT_TRUE(Lock.owns_lock())
875 << "Detected concurrent onDiagnosticsReady calls for the same file.";
877 // If we started the second parse immediately, it might cancel the first.
878 // So we don't allow it to start until the first has delivered diags...
879 if (FirstRequest) {
880 FirstRequest = false;
881 StartSecondReparse.set_value();
882 // ... but then we wait long enough that the callbacks would overlap.
883 std::this_thread::sleep_for(std::chrono::milliseconds(50));
887 private:
888 std::mutex Mutex;
889 bool FirstRequest = true;
890 std::promise<void> StartSecondReparse;
893 const auto *SourceContentsWithoutErrors = R"cpp(
894 int a;
895 int b;
896 int c;
897 int d;
898 )cpp";
900 const auto *SourceContentsWithErrors = R"cpp(
901 int a = x;
902 int b;
903 int c;
904 int d;
905 )cpp";
907 auto FooCpp = testPath("foo.cpp");
908 MockFS FS;
909 FS.Files[FooCpp] = "";
911 std::promise<void> StartSecondPromise;
912 std::future<void> StartSecond = StartSecondPromise.get_future();
914 NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise));
915 MockCompilationDatabase CDB;
916 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
917 Server.addDocument(FooCpp, SourceContentsWithErrors);
918 StartSecond.wait();
919 Server.addDocument(FooCpp, SourceContentsWithoutErrors);
920 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
921 ASSERT_EQ(DiagConsumer.Count, 2); // Sanity check - we actually ran both?
924 TEST(ClangdServerTest, FormatCode) {
925 MockFS FS;
926 ErrorCheckingCallbacks DiagConsumer;
927 MockCompilationDatabase CDB;
928 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
930 auto Path = testPath("foo.cpp");
931 std::string Code = R"cpp(
932 #include "x.h"
933 #include "y.h"
935 void f( ) {}
936 )cpp";
937 std::string Expected = R"cpp(
938 #include "x.h"
939 #include "y.h"
941 void f() {}
942 )cpp";
943 FS.Files[Path] = Code;
944 runAddDocument(Server, Path, Code);
946 auto Replaces = runFormatFile(Server, Path, /*Rng=*/std::nullopt);
947 EXPECT_TRUE(static_cast<bool>(Replaces));
948 auto Changed = tooling::applyAllReplacements(Code, *Replaces);
949 EXPECT_TRUE(static_cast<bool>(Changed));
950 EXPECT_EQ(Expected, *Changed);
953 TEST(ClangdServerTest, ChangedHeaderFromISystem) {
954 MockFS FS;
955 ErrorCheckingCallbacks DiagConsumer;
956 MockCompilationDatabase CDB;
957 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
959 auto SourcePath = testPath("source/foo.cpp");
960 auto HeaderPath = testPath("headers/foo.h");
961 FS.Files[HeaderPath] = "struct X { int bar; };";
962 Annotations Code(R"cpp(
963 #include "foo.h"
965 int main() {
966 X().ba^
967 })cpp");
968 CDB.ExtraClangFlags.push_back("-xc++");
969 CDB.ExtraClangFlags.push_back("-isystem" + testPath("headers"));
971 runAddDocument(Server, SourcePath, Code.code());
972 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
973 clangd::CodeCompleteOptions()))
974 .Completions;
975 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar")));
976 // Update the header and rerun addDocument to make sure we get the updated
977 // files.
978 FS.Files[HeaderPath] = "struct X { int bar; int baz; };";
979 runAddDocument(Server, SourcePath, Code.code());
980 Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
981 clangd::CodeCompleteOptions()))
982 .Completions;
983 // We want to make sure we see the updated version.
984 EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "bar"),
985 Field(&CodeCompletion::Name, "baz")));
988 // FIXME(ioeric): make this work for windows again.
989 #ifndef _WIN32
990 // Check that running code completion doesn't stat() a bunch of files from the
991 // preamble again. (They should be using the preamble's stat-cache)
992 TEST(ClangdTests, PreambleVFSStatCache) {
993 class StatRecordingFS : public ThreadsafeFS {
994 llvm::StringMap<unsigned> &CountStats;
996 public:
997 // If relative paths are used, they are resolved with testPath().
998 llvm::StringMap<std::string> Files;
1000 StatRecordingFS(llvm::StringMap<unsigned> &CountStats)
1001 : CountStats(CountStats) {}
1003 private:
1004 IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override {
1005 class StatRecordingVFS : public llvm::vfs::ProxyFileSystem {
1006 public:
1007 StatRecordingVFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
1008 llvm::StringMap<unsigned> &CountStats)
1009 : ProxyFileSystem(std::move(FS)), CountStats(CountStats) {}
1011 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
1012 openFileForRead(const Twine &Path) override {
1013 ++CountStats[llvm::sys::path::filename(Path.str())];
1014 return ProxyFileSystem::openFileForRead(Path);
1016 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
1017 ++CountStats[llvm::sys::path::filename(Path.str())];
1018 return ProxyFileSystem::status(Path);
1021 private:
1022 llvm::StringMap<unsigned> &CountStats;
1025 return IntrusiveRefCntPtr<StatRecordingVFS>(
1026 new StatRecordingVFS(buildTestFS(Files), CountStats));
1030 llvm::StringMap<unsigned> CountStats;
1031 StatRecordingFS FS(CountStats);
1032 ErrorCheckingCallbacks DiagConsumer;
1033 MockCompilationDatabase CDB;
1034 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1036 auto SourcePath = testPath("foo.cpp");
1037 auto HeaderPath = testPath("foo.h");
1038 FS.Files[HeaderPath] = "struct TestSym {};";
1039 Annotations Code(R"cpp(
1040 #include "foo.h"
1042 int main() {
1043 TestSy^
1044 })cpp");
1046 runAddDocument(Server, SourcePath, Code.code());
1048 unsigned Before = CountStats["foo.h"];
1049 EXPECT_GT(Before, 0u);
1050 auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(),
1051 clangd::CodeCompleteOptions()))
1052 .Completions;
1053 EXPECT_EQ(CountStats["foo.h"], Before);
1054 EXPECT_THAT(Completions,
1055 ElementsAre(Field(&CodeCompletion::Name, "TestSym")));
1057 #endif
1059 TEST(ClangdServerTest, FallbackWhenPreambleIsNotReady) {
1060 MockFS FS;
1061 ErrorCheckingCallbacks DiagConsumer;
1062 MockCompilationDatabase CDB;
1063 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1065 auto FooCpp = testPath("foo.cpp");
1066 Annotations Code(R"cpp(
1067 namespace ns { int xyz; }
1068 using namespace ns;
1069 int main() {
1071 })cpp");
1072 FS.Files[FooCpp] = FooCpp;
1074 auto Opts = clangd::CodeCompleteOptions();
1075 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1077 // This will make compile command broken and preamble absent.
1078 CDB.ExtraClangFlags = {"-###"};
1079 Server.addDocument(FooCpp, Code.code());
1080 ASSERT_TRUE(Server.blockUntilIdleForTest());
1081 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1082 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1083 // Identifier-based fallback completion doesn't know about "symbol" scope.
1084 EXPECT_THAT(Res.Completions,
1085 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1086 Field(&CodeCompletion::Scope, ""))));
1088 // Make the compile command work again.
1089 CDB.ExtraClangFlags = {"-std=c++11"};
1090 Server.addDocument(FooCpp, Code.code());
1091 ASSERT_TRUE(Server.blockUntilIdleForTest());
1092 EXPECT_THAT(
1093 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1094 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1095 Field(&CodeCompletion::Scope, "ns::"))));
1097 // Now force identifier-based completion.
1098 Opts.RunParser = CodeCompleteOptions::NeverParse;
1099 EXPECT_THAT(
1100 cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)).Completions,
1101 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1102 Field(&CodeCompletion::Scope, ""))));
1105 TEST(ClangdServerTest, FallbackWhenWaitingForCompileCommand) {
1106 MockFS FS;
1107 ErrorCheckingCallbacks DiagConsumer;
1108 // Returns compile command only when notified.
1109 class DelayedCompilationDatabase : public GlobalCompilationDatabase {
1110 public:
1111 DelayedCompilationDatabase(Notification &CanReturnCommand)
1112 : CanReturnCommand(CanReturnCommand) {}
1114 std::optional<tooling::CompileCommand>
1115 getCompileCommand(PathRef File) const override {
1116 // FIXME: make this timeout and fail instead of waiting forever in case
1117 // something goes wrong.
1118 CanReturnCommand.wait();
1119 auto FileName = llvm::sys::path::filename(File);
1120 std::vector<std::string> CommandLine = {"clangd", "-ffreestanding",
1121 std::string(File)};
1122 return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
1123 FileName, std::move(CommandLine), "")};
1126 std::vector<std::string> ExtraClangFlags;
1128 private:
1129 Notification &CanReturnCommand;
1132 Notification CanReturnCommand;
1133 DelayedCompilationDatabase CDB(CanReturnCommand);
1134 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1136 auto FooCpp = testPath("foo.cpp");
1137 Annotations Code(R"cpp(
1138 namespace ns { int xyz; }
1139 using namespace ns;
1140 int main() {
1142 })cpp");
1143 FS.Files[FooCpp] = FooCpp;
1144 Server.addDocument(FooCpp, Code.code());
1146 // Sleep for some time to make sure code completion is not run because update
1147 // hasn't been scheduled.
1148 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1149 auto Opts = clangd::CodeCompleteOptions();
1150 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1152 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1153 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
1155 CanReturnCommand.notify();
1156 ASSERT_TRUE(Server.blockUntilIdleForTest());
1157 EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
1158 clangd::CodeCompleteOptions()))
1159 .Completions,
1160 ElementsAre(AllOf(Field(&CodeCompletion::Name, "xyz"),
1161 Field(&CodeCompletion::Scope, "ns::"))));
1164 TEST(ClangdServerTest, CustomAction) {
1165 OverlayCDB CDB(/*Base=*/nullptr);
1166 MockFS FS;
1167 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
1169 Server.addDocument(testPath("foo.cc"), "void x();");
1170 Decl::Kind XKind = Decl::TranslationUnit;
1171 EXPECT_THAT_ERROR(runCustomAction(Server, testPath("foo.cc"),
1172 [&](InputsAndAST AST) {
1173 XKind = findDecl(AST.AST, "x").getKind();
1175 llvm::Succeeded());
1176 EXPECT_EQ(XKind, Decl::Function);
1179 // Tests fails when built with asan due to stack overflow. So skip running the
1180 // test as a workaround.
1181 #if !defined(__has_feature) || !__has_feature(address_sanitizer)
1182 TEST(ClangdServerTest, TestStackOverflow) {
1183 MockFS FS;
1184 ErrorCheckingCallbacks DiagConsumer;
1185 MockCompilationDatabase CDB;
1186 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1188 const char *SourceContents = R"cpp(
1189 constexpr int foo() { return foo(); }
1190 static_assert(foo());
1191 )cpp";
1193 auto FooCpp = testPath("foo.cpp");
1194 FS.Files[FooCpp] = SourceContents;
1196 Server.addDocument(FooCpp, SourceContents);
1197 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
1198 // check that we got a constexpr depth error, and not crashed by stack
1199 // overflow
1200 EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
1202 #endif
1204 TEST(ClangdServer, TidyOverrideTest) {
1205 struct DiagsCheckingCallback : public ClangdServer::Callbacks {
1206 public:
1207 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
1208 llvm::ArrayRef<Diag> Diagnostics) override {
1209 std::lock_guard<std::mutex> Lock(Mutex);
1210 HadDiagsInLastCallback = !Diagnostics.empty();
1213 std::mutex Mutex;
1214 bool HadDiagsInLastCallback = false;
1215 } DiagConsumer;
1217 MockFS FS;
1218 // These checks don't work well in clangd, even if configured they shouldn't
1219 // run.
1220 FS.Files[testPath(".clang-tidy")] = R"(
1221 Checks: -*,bugprone-use-after-move,llvm-header-guard
1223 MockCompilationDatabase CDB;
1224 std::vector<TidyProvider> Stack;
1225 Stack.push_back(provideClangTidyFiles(FS));
1226 Stack.push_back(disableUnusableChecks());
1227 TidyProvider Provider = combine(std::move(Stack));
1228 CDB.ExtraClangFlags = {"-xc++"};
1229 auto Opts = ClangdServer::optsForTest();
1230 Opts.ClangTidyProvider = Provider;
1231 ClangdServer Server(CDB, FS, Opts, &DiagConsumer);
1232 const char *SourceContents = R"cpp(
1233 struct Foo { Foo(); Foo(Foo&); Foo(Foo&&); };
1234 namespace std { Foo&& move(Foo&); }
1235 void foo() {
1236 Foo x;
1237 Foo y = std::move(x);
1238 Foo z = x;
1239 })cpp";
1240 Server.addDocument(testPath("foo.h"), SourceContents);
1241 ASSERT_TRUE(Server.blockUntilIdleForTest());
1242 EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback);
1245 TEST(ClangdServer, MemoryUsageTest) {
1246 MockFS FS;
1247 MockCompilationDatabase CDB;
1248 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
1250 auto FooCpp = testPath("foo.cpp");
1251 Server.addDocument(FooCpp, "");
1252 ASSERT_TRUE(Server.blockUntilIdleForTest());
1254 llvm::BumpPtrAllocator Alloc;
1255 MemoryTree MT(&Alloc);
1256 Server.profile(MT);
1257 ASSERT_TRUE(MT.children().count("tuscheduler"));
1258 EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp));
1261 TEST(ClangdServer, RespectsTweakFormatting) {
1262 static constexpr const char *TweakID = "ModuleTweak";
1263 static constexpr const char *NewContents = "{not;\nformatted;}";
1265 // Contributes a tweak that generates a non-formatted insertion and disables
1266 // formatting.
1267 struct TweakContributingModule final : public FeatureModule {
1268 struct ModuleTweak final : public Tweak {
1269 const char *id() const override { return TweakID; }
1270 bool prepare(const Selection &Sel) override { return true; }
1271 Expected<Effect> apply(const Selection &Sel) override {
1272 auto &SM = Sel.AST->getSourceManager();
1273 llvm::StringRef FilePath = SM.getFilename(Sel.Cursor);
1274 tooling::Replacements Reps;
1275 llvm::cantFail(
1276 Reps.add(tooling::Replacement(FilePath, 0, 0, NewContents)));
1277 auto E = llvm::cantFail(Effect::mainFileEdit(SM, std::move(Reps)));
1278 E.FormatEdits = false;
1279 return E;
1281 std::string title() const override { return id(); }
1282 llvm::StringLiteral kind() const override {
1283 return llvm::StringLiteral("");
1287 void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override {
1288 Out.emplace_back(new ModuleTweak);
1292 MockFS FS;
1293 MockCompilationDatabase CDB;
1294 auto Opts = ClangdServer::optsForTest();
1295 FeatureModuleSet Set;
1296 Set.add(std::make_unique<TweakContributingModule>());
1297 Opts.FeatureModules = &Set;
1298 ClangdServer Server(CDB, FS, Opts);
1300 auto FooCpp = testPath("foo.cpp");
1301 Server.addDocument(FooCpp, "");
1302 ASSERT_TRUE(Server.blockUntilIdleForTest());
1304 // Ensure that disabled formatting is respected.
1305 Notification N;
1306 Server.applyTweak(FooCpp, {}, TweakID, [&](llvm::Expected<Tweak::Effect> E) {
1307 ASSERT_TRUE(static_cast<bool>(E));
1308 EXPECT_THAT(llvm::cantFail(E->ApplyEdits.lookup(FooCpp).apply()),
1309 NewContents);
1310 N.notify();
1312 N.wait();
1315 TEST(ClangdServer, InactiveRegions) {
1316 struct InactiveRegionsCallback : ClangdServer::Callbacks {
1317 std::vector<std::vector<Range>> FoundInactiveRegions;
1319 void onInactiveRegionsReady(PathRef FIle,
1320 std::vector<Range> InactiveRegions) override {
1321 FoundInactiveRegions.push_back(std::move(InactiveRegions));
1325 MockFS FS;
1326 MockCompilationDatabase CDB;
1327 CDB.ExtraClangFlags.push_back("-DCMDMACRO");
1328 auto Opts = ClangdServer::optsForTest();
1329 Opts.PublishInactiveRegions = true;
1330 InactiveRegionsCallback Callback;
1331 ClangdServer Server(CDB, FS, Opts, &Callback);
1332 Annotations Source(R"cpp(
1333 #define PREAMBLEMACRO 42
1334 #if PREAMBLEMACRO > 40
1335 #define ACTIVE
1336 #else
1337 $inactive1[[ #define INACTIVE]]
1338 #endif
1339 int endPreamble;
1340 #ifndef CMDMACRO
1341 $inactive2[[ int inactiveInt;]]
1342 #endif
1343 #undef CMDMACRO
1344 #ifdef CMDMACRO
1345 $inactive3[[ int inactiveInt2;]]
1346 #elif PREAMBLEMACRO > 0
1347 int activeInt1;
1348 int activeInt2;
1349 #else
1350 $inactive4[[ int inactiveInt3;]]
1351 #endif
1352 #ifdef CMDMACRO
1353 #endif // empty inactive range, gets dropped
1354 )cpp");
1355 Server.addDocument(testPath("foo.cpp"), Source.code());
1356 ASSERT_TRUE(Server.blockUntilIdleForTest());
1357 EXPECT_THAT(Callback.FoundInactiveRegions,
1358 ElementsAre(ElementsAre(
1359 Source.range("inactive1"), Source.range("inactive2"),
1360 Source.range("inactive3"), Source.range("inactive4"))));
1363 } // namespace
1364 } // namespace clangd
1365 } // namespace clang