[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clangd / unittests / ClangdTests.cpp
blob643b8e9f12d751d6845a15516196c29081c686aa
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/Config/llvm-config.h" // for LLVM_ON_UNIX
33 #include "llvm/Support/Allocator.h"
34 #include "llvm/Support/Error.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/Regex.h"
37 #include "llvm/Support/VirtualFileSystem.h"
38 #include "llvm/Testing/Support/Error.h"
39 #include "gmock/gmock.h"
40 #include "gtest/gtest.h"
41 #include <algorithm>
42 #include <chrono>
43 #include <iostream>
44 #include <optional>
45 #include <random>
46 #include <string>
47 #include <thread>
48 #include <vector>
50 namespace clang {
51 namespace clangd {
53 namespace {
55 using ::testing::AllOf;
56 using ::testing::ElementsAre;
57 using ::testing::Field;
58 using ::testing::IsEmpty;
59 using ::testing::Pair;
60 using ::testing::SizeIs;
61 using ::testing::UnorderedElementsAre;
63 MATCHER_P2(DeclAt, File, Range, "") {
64 return arg.PreferredDeclaration ==
65 Location{URIForFile::canonicalize(File, testRoot()), Range};
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;
74 return false;
77 class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
78 public:
79 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
80 llvm::ArrayRef<Diag> Diagnostics) override {
81 bool HadError = diagsContainErrors(Diagnostics);
82 std::lock_guard<std::mutex> Lock(Mutex);
83 HadErrorInLastDiags = HadError;
86 bool hadErrorInLastDiags() {
87 std::lock_guard<std::mutex> Lock(Mutex);
88 return HadErrorInLastDiags;
91 private:
92 std::mutex Mutex;
93 bool HadErrorInLastDiags = false;
96 /// For each file, record whether the last published diagnostics contained at
97 /// least one error.
98 class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
99 public:
100 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
101 llvm::ArrayRef<Diag> Diagnostics) override {
102 bool HadError = diagsContainErrors(Diagnostics);
104 std::lock_guard<std::mutex> Lock(Mutex);
105 LastDiagsHadError[File] = HadError;
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.
111 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;
119 void clear() {
120 std::lock_guard<std::mutex> Lock(Mutex);
121 LastDiagsHadError.clear();
124 private:
125 mutable std::mutex Mutex;
126 llvm::StringMap<bool> LastDiagsHadError;
129 /// Replaces all patterns of the form 0x123abc with spaces
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;
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();
140 Result += Pending.take_front(MatchPos);
141 Pending = Pending.drop_front(MatchPos + Matches[0].size());
143 Result += Pending;
145 return Result;
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>";
159 Done.notify();
161 Done.wait();
162 return Result;
165 std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
166 return replacePtrsInDump(dumpAST(Server, File));
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);
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;
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);
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);
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);
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);
224 TEST(ClangdServerTest, Reparse) {
225 MockFS FS;
226 ErrorCheckingCallbacks DiagConsumer;
227 MockCompilationDatabase CDB;
228 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
230 const auto *SourceContents = R"cpp(
231 #include "foo.h"
232 int b = a;
233 )cpp";
235 auto FooCpp = testPath("foo.cpp");
237 FS.Files[testPath("foo.h")] = "int a;";
238 FS.Files[FooCpp] = SourceContents;
240 Server.addDocument(FooCpp, SourceContents);
241 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
242 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
243 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
245 Server.addDocument(FooCpp, "");
246 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
247 auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
248 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
250 Server.addDocument(FooCpp, SourceContents);
251 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
252 auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
253 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
255 EXPECT_EQ(DumpParse1, DumpParse2);
256 EXPECT_NE(DumpParse1, DumpParseEmpty);
259 TEST(ClangdServerTest, ReparseOnHeaderChange) {
260 MockFS FS;
261 ErrorCheckingCallbacks DiagConsumer;
262 MockCompilationDatabase CDB;
263 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
265 const auto *SourceContents = R"cpp(
266 #include "foo.h"
267 int b = a;
268 )cpp";
270 auto FooCpp = testPath("foo.cpp");
271 auto FooH = testPath("foo.h");
273 FS.Files[FooH] = "int a;";
274 FS.Files[FooCpp] = SourceContents;
276 Server.addDocument(FooCpp, SourceContents);
277 ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics";
278 auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
279 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
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());
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());
293 EXPECT_EQ(DumpParse1, DumpParse2);
294 EXPECT_NE(DumpParse1, DumpParseDifferent);
297 TEST(ClangdServerTest, PropagatesContexts) {
298 static Key<int> Secret;
299 struct ContextReadingFS : public ThreadsafeFS {
300 mutable int Got;
302 private:
303 IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override {
304 Got = Context::current().getExisting(Secret);
305 return buildTestFS({});
307 } FS;
308 struct Callbacks : public ClangdServer::Callbacks {
309 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
310 llvm::ArrayRef<Diag> Diagnostics) override {
311 Got = Context::current().getExisting(Secret);
313 int Got;
314 } Callbacks;
315 MockCompilationDatabase CDB;
317 // Verify that the context is plumbed to the FS provider and diagnostics.
318 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &Callbacks);
320 WithContextValue Entrypoint(Secret, 42);
321 Server.addDocument(testPath("foo.cpp"), "void main(){}");
323 ASSERT_TRUE(Server.blockUntilIdleForTest());
324 EXPECT_EQ(FS.Got, 42);
325 EXPECT_EQ(Callbacks.Got, 42);
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)};
348 } CfgProvider;
350 auto Opts = ClangdServer::optsForTest();
351 Opts.ContextProvider =
352 ClangdServer::createConfiguredContextProvider(&CfgProvider, nullptr);
353 OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{},
354 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());
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 llvm::ArrayRef<Diag> Diagnostics) override {
377 Got = Version.str();
379 std::string Got = "";
380 } Callbacks;
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");
388 // Only enable this test on Unix
389 #ifdef LLVM_ON_UNIX
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-unknown-linux-gnu",
397 "-m64", "--gcc-toolchain=/randomusr",
398 "-stdlib=libstdc++"});
399 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
401 // Just a random gcc version string
402 SmallString<8> Version("4.9.3");
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);
408 // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
409 // installation there.
410 SmallString<64> MockLibFile;
411 llvm::sys::path::append(MockLibFile, LibDir, "64", "crtbegin.o");
412 FS.Files[MockLibFile] = "";
414 SmallString<64> IncludeDir("/randomusr/include/c++");
415 llvm::sys::path::append(IncludeDir, Version);
417 SmallString<64> StringPath;
418 llvm::sys::path::append(StringPath, IncludeDir, "string");
419 FS.Files[StringPath] = "class mock_string {};";
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;
428 runAddDocument(Server, FooCpp, SourceContents);
429 EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
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());
438 #endif // LLVM_ON_UNIX
440 TEST(ClangdServerTest, ForceReparseCompileCommand) {
441 MockFS FS;
442 ErrorCheckingCallbacks DiagConsumer;
443 MockCompilationDatabase CDB;
444 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
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";
456 FS.Files[FooCpp] = "";
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());
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());
476 TEST(ClangdServerTest, ForceReparseCompileCommandDefines) {
477 MockFS FS;
478 ErrorCheckingCallbacks DiagConsumer;
479 MockCompilationDatabase CDB;
480 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
482 auto FooCpp = testPath("foo.cpp");
483 const auto *SourceContents = R"cpp(
484 #ifdef WITH_ERROR
485 this
486 #endif
488 int main() { return 0; }
489 )cpp";
490 FS.Files[FooCpp] = "";
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());
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());
507 // Test ClangdServer.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
516 int main () { bo^b (); return 0; }
517 )cpp");
519 Annotations BarSource(R"cpp(
520 #ifdef MACRO
521 this is an error
522 #endif
523 )cpp");
525 Annotations BazSource(R"cpp(
526 int hello;
527 )cpp");
529 MockFS FS;
530 MockCompilationDatabase CDB;
531 MultipleErrorCheckingCallbacks DiagConsumer;
532 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
534 auto FooCpp = testPath("foo.cpp");
535 auto BarCpp = testPath("bar.cpp");
536 auto BazCpp = testPath("baz.cpp");
538 FS.Files[FooCpp] = "";
539 FS.Files[BarCpp] = "";
540 FS.Files[BazCpp] = "";
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());
548 EXPECT_THAT(DiagConsumer.filesWithDiags(),
549 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, true),
550 Pair(BazCpp, false)));
552 auto Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
553 EXPECT_TRUE(bool(Locations));
554 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("one"))));
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());
564 EXPECT_THAT(DiagConsumer.filesWithDiags(),
565 UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, false)));
567 Locations = runLocateSymbolAt(Server, FooCpp, FooSource.point());
568 EXPECT_TRUE(bool(Locations));
569 EXPECT_THAT(*Locations, ElementsAre(DeclAt(FooCpp, FooSource.range("two"))));
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);
580 TEST(ClangdServerTest, FileStats) {
581 MockFS FS;
582 ErrorCheckingCallbacks DiagConsumer;
583 MockCompilationDatabase CDB;
584 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
586 Path FooCpp = testPath("foo.cpp");
587 const auto *SourceContents = R"cpp(
588 struct Something {
589 int method();
591 )cpp";
592 Path BarCpp = testPath("bar.cpp");
594 FS.Files[FooCpp] = "";
595 FS.Files[BarCpp] = "";
597 EXPECT_THAT(Server.fileStats(), IsEmpty());
599 Server.addDocument(FooCpp, SourceContents);
600 Server.addDocument(BarCpp, SourceContents);
601 ASSERT_TRUE(Server.blockUntilIdleForTest());
603 EXPECT_THAT(Server.fileStats(),
604 UnorderedElementsAre(Stats(FooCpp, true, 1, 1),
605 Stats(BarCpp, true, 1, 1)));
607 Server.removeDocument(FooCpp);
608 ASSERT_TRUE(Server.blockUntilIdleForTest());
609 EXPECT_THAT(Server.fileStats(), ElementsAre(Stats(BarCpp, true, 1, 1)));
611 Server.removeDocument(BarCpp);
612 ASSERT_TRUE(Server.blockUntilIdleForTest());
613 EXPECT_THAT(Server.fileStats(), IsEmpty());
616 TEST(ClangdServerTest, InvalidCompileCommand) {
617 MockFS FS;
618 ErrorCheckingCallbacks DiagConsumer;
619 MockCompilationDatabase CDB;
621 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
623 auto FooCpp = testPath("foo.cpp");
624 // clang cannot create CompilerInvocation in this case.
625 CDB.ExtraClangFlags.push_back("-###");
627 // Clang can't parse command args in that case, but we shouldn't crash.
628 runAddDocument(Server, FooCpp, "int main() {}");
630 EXPECT_EQ(dumpAST(Server, FooCpp), "<no-ast>");
631 EXPECT_ERROR(runLocateSymbolAt(Server, FooCpp, Position()));
632 EXPECT_ERROR(runFindDocumentHighlights(Server, FooCpp, Position()));
633 EXPECT_ERROR(runRename(Server, FooCpp, Position(), "new_name",
634 clangd::RenameOptions()));
635 EXPECT_ERROR(
636 runSignatureHelp(Server, FooCpp, Position(), MarkupKind::PlainText));
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")));
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;
654 const auto *SourceContentsWithoutErrors = R"cpp(
655 int a;
656 int b;
657 int c;
658 int d;
659 )cpp";
661 const auto *SourceContentsWithErrors = R"cpp(
662 int a = x;
663 int b;
664 int c;
665 int d;
666 )cpp";
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;
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));
682 struct FileStat {
683 unsigned HitsWithoutErrors = 0;
684 unsigned HitsWithErrors = 0;
685 bool HadErrorsInLastDiags = false;
688 class TestDiagConsumer : public ClangdServer::Callbacks {
689 public:
690 TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
692 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
693 llvm::ArrayRef<Diag> Diagnostics) override {
694 StringRef FileIndexStr = llvm::sys::path::stem(File);
695 ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
697 unsigned long FileIndex = std::stoul(FileIndexStr.str());
699 bool HadError = diagsContainErrors(Diagnostics);
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;
709 std::vector<FileStat> takeFileStats() {
710 std::lock_guard<std::mutex> Lock(Mutex);
711 return std::move(Stats);
714 private:
715 std::mutex Mutex;
716 std::vector<FileStat> Stats;
719 struct RequestStats {
720 unsigned RequestsWithoutErrors = 0;
721 unsigned RequestsWithErrors = 0;
722 bool LastContentsHadErrors = false;
723 bool FileIsRemoved = true;
726 std::vector<RequestStats> ReqStats;
727 ReqStats.reserve(FilesCount);
728 for (unsigned FileIndex = 0; FileIndex < FilesCount; ++FileIndex)
729 ReqStats.emplace_back();
731 TestDiagConsumer DiagConsumer;
733 MockCompilationDatabase CDB;
734 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
736 // Prepare some random distributions for the test.
737 std::random_device RandGen;
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);
747 // Some helpers.
748 auto UpdateStatsOnAddDocument = [&](unsigned FileIndex, bool HadErrors) {
749 auto &Stats = ReqStats[FileIndex];
751 if (HadErrors)
752 ++Stats.RequestsWithErrors;
753 else
754 ++Stats.RequestsWithoutErrors;
755 Stats.LastContentsHadErrors = HadErrors;
756 Stats.FileIsRemoved = false;
759 auto UpdateStatsOnRemoveDocument = [&](unsigned FileIndex) {
760 auto &Stats = ReqStats[FileIndex];
762 Stats.FileIsRemoved = true;
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);
773 // Various requests that we would randomly run.
774 auto AddDocumentRequest = [&]() {
775 unsigned FileIndex = FileIndexDist(RandGen);
776 AddDocument(FileIndex, /*SkipCache=*/false);
779 auto ForceReparseRequest = [&]() {
780 unsigned FileIndex = FileIndexDist(RandGen);
781 AddDocument(FileIndex, /*SkipCache=*/true);
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);
790 Server.removeDocument(FilePaths[FileIndex]);
791 UpdateStatsOnRemoveDocument(FileIndex);
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);
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()));
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);
819 Position Pos;
820 Pos.line = LineDist(RandGen);
821 Pos.character = ColumnDist(RandGen);
823 ASSERT_TRUE(!!runLocateSymbolAt(Server, FilePaths[FileIndex], Pos));
826 std::vector<std::function<void()>> AsyncRequests = {
827 AddDocumentRequest, ForceReparseRequest, RemoveDocumentRequest};
828 std::vector<std::function<void()>> BlockingRequests = {
829 CodeCompletionRequest, LocateSymbolRequest};
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]();
847 ASSERT_TRUE(Server.blockUntilIdleForTest());
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);
858 ASSERT_LE(Stats[I].HitsWithErrors, ReqStats[I].RequestsWithErrors);
859 ASSERT_LE(Stats[I].HitsWithoutErrors, ReqStats[I].RequestsWithoutErrors);
863 TEST(ClangdThreadingTest, NoConcurrentDiagnostics) {
864 class NoConcurrentAccessDiagConsumer : public ClangdServer::Callbacks {
865 public:
866 std::atomic<int> Count = {0};
868 NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
869 : StartSecondReparse(std::move(StartSecondReparse)) {}
871 void onDiagnosticsReady(PathRef, llvm::StringRef,
872 llvm::ArrayRef<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.";
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));
888 private:
889 std::mutex Mutex;
890 bool FirstRequest = true;
891 std::promise<void> StartSecondReparse;
894 const auto *SourceContentsWithoutErrors = R"cpp(
895 int a;
896 int b;
897 int c;
898 int d;
899 )cpp";
901 const auto *SourceContentsWithErrors = R"cpp(
902 int a = x;
903 int b;
904 int c;
905 int d;
906 )cpp";
908 auto FooCpp = testPath("foo.cpp");
909 MockFS FS;
910 FS.Files[FooCpp] = "";
912 std::promise<void> StartSecondPromise;
913 std::future<void> StartSecond = StartSecondPromise.get_future();
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?
925 TEST(ClangdServerTest, FormatCode) {
926 MockFS FS;
927 ErrorCheckingCallbacks DiagConsumer;
928 MockCompilationDatabase CDB;
929 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
931 auto Path = testPath("foo.cpp");
932 std::string Code = R"cpp(
933 #include "x.h"
934 #include "y.h"
936 void f( ) {}
937 )cpp";
938 std::string Expected = R"cpp(
939 #include "x.h"
940 #include "y.h"
942 void f() {}
943 )cpp";
944 FS.Files[Path] = Code;
945 runAddDocument(Server, Path, Code);
947 auto Replaces = runFormatFile(Server, Path, /*Rng=*/std::nullopt);
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);
954 TEST(ClangdServerTest, ChangedHeaderFromISystem) {
955 MockFS FS;
956 ErrorCheckingCallbacks DiagConsumer;
957 MockCompilationDatabase CDB;
958 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
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"
966 int main() {
967 X().ba^
968 })cpp");
969 CDB.ExtraClangFlags.push_back("-xc++");
970 CDB.ExtraClangFlags.push_back("-isystem" + testPath("headers"));
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")));
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)
993 TEST(ClangdTests, PreambleVFSStatCache) {
994 class StatRecordingFS : public ThreadsafeFS {
995 llvm::StringMap<unsigned> &CountStats;
997 public:
998 // If relative paths are used, they are resolved with testPath().
999 llvm::StringMap<std::string> Files;
1001 StatRecordingFS(llvm::StringMap<unsigned> &CountStats)
1002 : CountStats(CountStats) {}
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) {}
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);
1017 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
1018 ++CountStats[llvm::sys::path::filename(Path.str())];
1019 return ProxyFileSystem::status(Path);
1022 private:
1023 llvm::StringMap<unsigned> &CountStats;
1026 return IntrusiveRefCntPtr<StatRecordingVFS>(
1027 new StatRecordingVFS(buildTestFS(Files), CountStats));
1031 llvm::StringMap<unsigned> CountStats;
1032 StatRecordingFS FS(CountStats);
1033 ErrorCheckingCallbacks DiagConsumer;
1034 MockCompilationDatabase CDB;
1035 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
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"
1043 int main() {
1044 TestSy^
1045 })cpp");
1047 runAddDocument(Server, SourcePath, Code.code());
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")));
1058 #endif
1060 TEST(ClangdServerTest, FallbackWhenPreambleIsNotReady) {
1061 MockFS FS;
1062 ErrorCheckingCallbacks DiagConsumer;
1063 MockCompilationDatabase CDB;
1064 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1066 auto FooCpp = testPath("foo.cpp");
1067 Annotations Code(R"cpp(
1068 namespace ns { int xyz; }
1069 using namespace ns;
1070 int main() {
1072 })cpp");
1073 FS.Files[FooCpp] = FooCpp;
1075 auto Opts = clangd::CodeCompleteOptions();
1076 Opts.RunParser = CodeCompleteOptions::ParseIfReady;
1078 // This will make compile command broken and preamble absent.
1079 CDB.ExtraClangFlags = {"-###"};
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, ""))));
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::"))));
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, ""))));
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) {}
1115 std::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), "")};
1127 std::vector<std::string> ExtraClangFlags;
1129 private:
1130 Notification &CanReturnCommand;
1133 Notification CanReturnCommand;
1134 DelayedCompilationDatabase CDB(CanReturnCommand);
1135 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1137 auto FooCpp = testPath("foo.cpp");
1138 Annotations Code(R"cpp(
1139 namespace ns { int xyz; }
1140 using namespace ns;
1141 int main() {
1143 })cpp");
1144 FS.Files[FooCpp] = FooCpp;
1145 Server.addDocument(FooCpp, Code.code());
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;
1153 auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts));
1154 EXPECT_EQ(Res.Context, CodeCompletionContext::CCC_Recovery);
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::"))));
1165 TEST(ClangdServerTest, CustomAction) {
1166 OverlayCDB CDB(/*Base=*/nullptr);
1167 MockFS FS;
1168 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
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();
1176 llvm::Succeeded());
1177 EXPECT_EQ(XKind, Decl::Function);
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)
1183 TEST(ClangdServerTest, TestStackOverflow) {
1184 MockFS FS;
1185 ErrorCheckingCallbacks DiagConsumer;
1186 MockCompilationDatabase CDB;
1187 ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
1189 const char *SourceContents = R"cpp(
1190 constexpr int foo() { return foo(); }
1191 static_assert(foo());
1192 )cpp";
1194 auto FooCpp = testPath("foo.cpp");
1195 FS.Files[FooCpp] = SourceContents;
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());
1203 #endif
1205 TEST(ClangdServer, TidyOverrideTest) {
1206 struct DiagsCheckingCallback : public ClangdServer::Callbacks {
1207 public:
1208 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
1209 llvm::ArrayRef<Diag> Diagnostics) override {
1210 std::lock_guard<std::mutex> Lock(Mutex);
1211 HadDiagsInLastCallback = !Diagnostics.empty();
1214 std::mutex Mutex;
1215 bool HadDiagsInLastCallback = false;
1216 } DiagConsumer;
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
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);
1246 TEST(ClangdServer, MemoryUsageTest) {
1247 MockFS FS;
1248 MockCompilationDatabase CDB;
1249 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
1251 auto FooCpp = testPath("foo.cpp");
1252 Server.addDocument(FooCpp, "");
1253 ASSERT_TRUE(Server.blockUntilIdleForTest());
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));
1262 TEST(ClangdServer, RespectsTweakFormatting) {
1263 static constexpr const char *TweakID = "ModuleTweak";
1264 static constexpr const char *NewContents = "{not;\nformatted;}";
1266 // Contributes a tweak that generates a non-formatted insertion and disables
1267 // formatting.
1268 struct TweakContributingModule final : public FeatureModule {
1269 struct ModuleTweak final : public Tweak {
1270 const char *id() const override { return TweakID; }
1271 bool prepare(const Selection &Sel) override { return true; }
1272 Expected<Effect> apply(const Selection &Sel) override {
1273 auto &SM = Sel.AST->getSourceManager();
1274 llvm::StringRef FilePath = SM.getFilename(Sel.Cursor);
1275 tooling::Replacements Reps;
1276 llvm::cantFail(
1277 Reps.add(tooling::Replacement(FilePath, 0, 0, NewContents)));
1278 auto E = llvm::cantFail(Effect::mainFileEdit(SM, std::move(Reps)));
1279 E.FormatEdits = false;
1280 return E;
1282 std::string title() const override { return id(); }
1283 llvm::StringLiteral kind() const override {
1284 return llvm::StringLiteral("");
1288 void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override {
1289 Out.emplace_back(new ModuleTweak);
1293 MockFS FS;
1294 MockCompilationDatabase CDB;
1295 auto Opts = ClangdServer::optsForTest();
1296 FeatureModuleSet Set;
1297 Set.add(std::make_unique<TweakContributingModule>());
1298 Opts.FeatureModules = &Set;
1299 ClangdServer Server(CDB, FS, Opts);
1301 auto FooCpp = testPath("foo.cpp");
1302 Server.addDocument(FooCpp, "");
1303 ASSERT_TRUE(Server.blockUntilIdleForTest());
1305 // Ensure that disabled formatting is respected.
1306 Notification N;
1307 Server.applyTweak(FooCpp, {}, TweakID, [&](llvm::Expected<Tweak::Effect> E) {
1308 ASSERT_TRUE(static_cast<bool>(E));
1309 EXPECT_THAT(llvm::cantFail(E->ApplyEdits.lookup(FooCpp).apply()),
1310 NewContents);
1311 N.notify();
1313 N.wait();
1316 TEST(ClangdServer, InactiveRegions) {
1317 struct InactiveRegionsCallback : ClangdServer::Callbacks {
1318 std::vector<std::vector<Range>> FoundInactiveRegions;
1320 void onInactiveRegionsReady(PathRef FIle,
1321 std::vector<Range> InactiveRegions) override {
1322 FoundInactiveRegions.push_back(std::move(InactiveRegions));
1326 MockFS FS;
1327 MockCompilationDatabase CDB;
1328 CDB.ExtraClangFlags.push_back("-DCMDMACRO");
1329 auto Opts = ClangdServer::optsForTest();
1330 Opts.PublishInactiveRegions = true;
1331 InactiveRegionsCallback Callback;
1332 ClangdServer Server(CDB, FS, Opts, &Callback);
1333 Annotations Source(R"cpp(
1334 #define PREAMBLEMACRO 42
1335 #if PREAMBLEMACRO > 40
1336 #define ACTIVE
1337 #else
1338 $inactive1[[ #define INACTIVE]]
1339 #endif
1340 int endPreamble;
1341 #ifndef CMDMACRO
1342 $inactive2[[ int inactiveInt;]]
1343 #endif
1344 #undef CMDMACRO
1345 #ifdef CMDMACRO
1346 $inactive3[[ int inactiveInt2;]]
1347 #elif PREAMBLEMACRO > 0
1348 int activeInt1;
1349 int activeInt2;
1350 #else
1351 $inactive4[[ int inactiveInt3;]]
1352 #endif
1353 #ifdef CMDMACRO
1354 #endif // empty inactive range, gets dropped
1355 )cpp");
1356 Server.addDocument(testPath("foo.cpp"), Source.code());
1357 ASSERT_TRUE(Server.blockUntilIdleForTest());
1358 EXPECT_THAT(Callback.FoundInactiveRegions,
1359 ElementsAre(ElementsAre(
1360 Source.range("inactive1"), Source.range("inactive2"),
1361 Source.range("inactive3"), Source.range("inactive4"))));
1364 } // namespace
1365 } // namespace clangd
1366 } // namespace clang