clang-tools  11.0.0
TUSchedulerTests.cpp
Go to the documentation of this file.
1 //===-- TUSchedulerTests.cpp ------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "ClangdServer.h"
11 #include "Diagnostics.h"
12 #include "Matchers.h"
13 #include "ParsedAST.h"
14 #include "Preamble.h"
15 #include "TUScheduler.h"
16 #include "TestFS.h"
17 #include "support/Cancellation.h"
18 #include "support/Context.h"
19 #include "support/Path.h"
20 #include "support/TestTracer.h"
21 #include "support/Threading.h"
22 #include "support/ThreadsafeFS.h"
23 #include "clang/Basic/DiagnosticDriver.h"
24 #include "llvm/ADT/ArrayRef.h"
25 #include "llvm/ADT/FunctionExtras.h"
26 #include "llvm/ADT/STLExtras.h"
27 #include "llvm/ADT/ScopeExit.h"
28 #include "llvm/ADT/StringExtras.h"
29 #include "llvm/ADT/StringMap.h"
30 #include "llvm/ADT/StringRef.h"
31 #include "gmock/gmock.h"
32 #include "gtest/gtest.h"
33 #include <algorithm>
34 #include <atomic>
35 #include <chrono>
36 #include <cstdint>
37 #include <memory>
38 #include <string>
39 #include <utility>
40 
41 namespace clang {
42 namespace clangd {
43 namespace {
44 
45 using ::testing::AnyOf;
46 using ::testing::Each;
47 using ::testing::ElementsAre;
48 using ::testing::Eq;
49 using ::testing::Field;
50 using ::testing::IsEmpty;
51 using ::testing::Pointee;
52 using ::testing::SizeIs;
53 using ::testing::UnorderedElementsAre;
54 
55 MATCHER_P2(TUState, PreambleActivity, ASTActivity, "") {
56  if (arg.PreambleActivity != PreambleActivity) {
57  *result_listener << "preamblestate is "
58  << static_cast<uint8_t>(arg.PreambleActivity);
59  return false;
60  }
61  if (arg.ASTActivity.K != ASTActivity) {
62  *result_listener << "aststate is " << arg.ASTActivity.K;
63  return false;
64  }
65  return true;
66 }
67 
68 // Dummy ContextProvider to verify the provider is invoked & contexts are used.
69 static Key<std::string> BoundPath;
70 Context bindPath(PathRef F) {
71  return Context::current().derive(BoundPath, F.str());
72 }
73 llvm::StringRef boundPath() {
74  const std::string *V = Context::current().get(BoundPath);
75  return V ? *V : llvm::StringRef("");
76 }
77 
78 TUScheduler::Options optsForTest() {
79  TUScheduler::Options Opts(ClangdServer::optsForTest());
80  Opts.ContextProvider = bindPath;
81  return Opts;
82 }
83 
84 class TUSchedulerTests : public ::testing::Test {
85 protected:
86  ParseInputs getInputs(PathRef File, std::string Contents) {
87  ParseInputs Inputs;
88  Inputs.CompileCommand = *CDB.getCompileCommand(File);
89  Inputs.TFS = &FS;
90  Inputs.Contents = std::move(Contents);
91  Inputs.Opts = ParseOptions();
92  return Inputs;
93  }
94 
95  void updateWithCallback(TUScheduler &S, PathRef File,
96  llvm::StringRef Contents, WantDiagnostics WD,
97  llvm::unique_function<void()> CB) {
98  updateWithCallback(S, File, getInputs(File, std::string(Contents)), WD,
99  std::move(CB));
100  }
101 
102  void updateWithCallback(TUScheduler &S, PathRef File, ParseInputs Inputs,
103  WantDiagnostics WD,
104  llvm::unique_function<void()> CB) {
105  WithContextValue Ctx(llvm::make_scope_exit(std::move(CB)));
106  S.update(File, Inputs, WD);
107  }
108 
109  static Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
111 
112  /// A diagnostics callback that should be passed to TUScheduler when it's used
113  /// in updateWithDiags.
114  static std::unique_ptr<ParsingCallbacks> captureDiags() {
115  class CaptureDiags : public ParsingCallbacks {
116  public:
117  void onMainAST(PathRef File, ParsedAST &AST, PublishFn Publish) override {
118  reportDiagnostics(File, AST.getDiagnostics(), Publish);
119  }
120 
121  void onFailedAST(PathRef File, llvm::StringRef Version,
122  std::vector<Diag> Diags, PublishFn Publish) override {
123  reportDiagnostics(File, Diags, Publish);
124  }
125 
126  private:
127  void reportDiagnostics(PathRef File, llvm::ArrayRef<Diag> Diags,
128  PublishFn Publish) {
130  if (!D)
131  return;
132  Publish([&]() {
133  const_cast<
134  llvm::unique_function<void(PathRef, std::vector<Diag>)> &> (*D)(
135  File, std::move(Diags));
136  });
137  }
138  };
139  return std::make_unique<CaptureDiags>();
140  }
141 
142  /// Schedule an update and call \p CB with the diagnostics it produces, if
143  /// any. The TUScheduler should be created with captureDiags as a
144  /// DiagsCallback for this to work.
145  void updateWithDiags(TUScheduler &S, PathRef File, ParseInputs Inputs,
146  WantDiagnostics WD,
147  llvm::unique_function<void(std::vector<Diag>)> CB) {
148  Path OrigFile = File.str();
149  WithContextValue Ctx(DiagsCallbackKey,
150  [OrigFile, CB = std::move(CB)](
151  PathRef File, std::vector<Diag> Diags) mutable {
152  assert(File == OrigFile);
153  CB(std::move(Diags));
154  });
155  S.update(File, std::move(Inputs), WD);
156  }
157 
158  void updateWithDiags(TUScheduler &S, PathRef File, llvm::StringRef Contents,
159  WantDiagnostics WD,
160  llvm::unique_function<void(std::vector<Diag>)> CB) {
161  return updateWithDiags(S, File, getInputs(File, std::string(Contents)), WD,
162  std::move(CB));
163  }
164 
165  MockFS FS;
166  MockCompilationDatabase CDB;
167 };
168 
169 Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
171 
172 TEST_F(TUSchedulerTests, MissingFiles) {
173  TUScheduler S(CDB, optsForTest());
174 
175  auto Added = testPath("added.cpp");
176  FS.Files[Added] = "x";
177 
178  auto Missing = testPath("missing.cpp");
179  FS.Files[Missing] = "";
180 
181  S.update(Added, getInputs(Added, "x"), WantDiagnostics::No);
182 
183  // Assert each operation for missing file is an error (even if it's
184  // available in VFS).
185  S.runWithAST("", Missing,
186  [&](Expected<InputsAndAST> AST) { EXPECT_ERROR(AST); });
187  S.runWithPreamble(
188  "", Missing, TUScheduler::Stale,
189  [&](Expected<InputsAndPreamble> Preamble) { EXPECT_ERROR(Preamble); });
190  // remove() shouldn't crash on missing files.
191  S.remove(Missing);
192 
193  // Assert there aren't any errors for added file.
194  S.runWithAST("", Added,
195  [&](Expected<InputsAndAST> AST) { EXPECT_TRUE(bool(AST)); });
196  S.runWithPreamble("", Added, TUScheduler::Stale,
197  [&](Expected<InputsAndPreamble> Preamble) {
198  EXPECT_TRUE(bool(Preamble));
199  });
200  S.remove(Added);
201 
202  // Assert that all operations fail after removing the file.
203  S.runWithAST("", Added,
204  [&](Expected<InputsAndAST> AST) { EXPECT_ERROR(AST); });
205  S.runWithPreamble("", Added, TUScheduler::Stale,
206  [&](Expected<InputsAndPreamble> Preamble) {
207  ASSERT_FALSE(bool(Preamble));
208  llvm::consumeError(Preamble.takeError());
209  });
210  // remove() shouldn't crash on missing files.
211  S.remove(Added);
212 }
213 
214 TEST_F(TUSchedulerTests, WantDiagnostics) {
215  std::atomic<int> CallbackCount(0);
216  {
217  // To avoid a racy test, don't allow tasks to actually run on the worker
218  // thread until we've scheduled them all.
219  Notification Ready;
220  TUScheduler S(CDB, optsForTest(), captureDiags());
221  auto Path = testPath("foo.cpp");
222  updateWithDiags(S, Path, "", WantDiagnostics::Yes,
223  [&](std::vector<Diag>) { Ready.wait(); });
224  updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes,
225  [&](std::vector<Diag>) { ++CallbackCount; });
226  updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto,
227  [&](std::vector<Diag>) {
228  ADD_FAILURE()
229  << "auto should have been cancelled by auto";
230  });
231  updateWithDiags(S, Path, "request no diags", WantDiagnostics::No,
232  [&](std::vector<Diag>) {
233  ADD_FAILURE() << "no diags should not be called back";
234  });
235  updateWithDiags(S, Path, "auto (produces)", WantDiagnostics::Auto,
236  [&](std::vector<Diag>) { ++CallbackCount; });
237  Ready.notify();
238 
239  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
240  }
241  EXPECT_EQ(2, CallbackCount);
242 }
243 
244 TEST_F(TUSchedulerTests, Debounce) {
245  std::atomic<int> CallbackCount(0);
246  {
247  auto Opts = optsForTest();
248  Opts.UpdateDebounce = DebouncePolicy::fixed(std::chrono::seconds(1));
249  TUScheduler S(CDB, Opts, captureDiags());
250  // FIXME: we could probably use timeouts lower than 1 second here.
251  auto Path = testPath("foo.cpp");
252  updateWithDiags(S, Path, "auto (debounced)", WantDiagnostics::Auto,
253  [&](std::vector<Diag>) {
254  ADD_FAILURE()
255  << "auto should have been debounced and canceled";
256  });
257  std::this_thread::sleep_for(std::chrono::milliseconds(200));
258  updateWithDiags(S, Path, "auto (timed out)", WantDiagnostics::Auto,
259  [&](std::vector<Diag>) { ++CallbackCount; });
260  std::this_thread::sleep_for(std::chrono::seconds(2));
261  updateWithDiags(S, Path, "auto (shut down)", WantDiagnostics::Auto,
262  [&](std::vector<Diag>) { ++CallbackCount; });
263 
264  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
265  }
266  EXPECT_EQ(2, CallbackCount);
267 }
268 
269 TEST_F(TUSchedulerTests, Cancellation) {
270  // We have the following update/read sequence
271  // U0
272  // U1(WantDiags=Yes) <-- cancelled
273  // R1 <-- cancelled
274  // U2(WantDiags=Yes) <-- cancelled
275  // R2A <-- cancelled
276  // R2B
277  // U3(WantDiags=Yes)
278  // R3 <-- cancelled
279  std::vector<std::string> DiagsSeen, ReadsSeen, ReadsCanceled;
280  {
281  Notification Proceed; // Ensure we schedule everything.
282  TUScheduler S(CDB, optsForTest(), captureDiags());
283  auto Path = testPath("foo.cpp");
284  // Helper to schedule a named update and return a function to cancel it.
285  auto Update = [&](std::string ID) -> Canceler {
286  auto T = cancelableTask();
287  WithContext C(std::move(T.first));
288  updateWithDiags(
289  S, Path, "//" + ID, WantDiagnostics::Yes,
290  [&, ID](std::vector<Diag> Diags) { DiagsSeen.push_back(ID); });
291  return std::move(T.second);
292  };
293  // Helper to schedule a named read and return a function to cancel it.
294  auto Read = [&](std::string ID) -> Canceler {
295  auto T = cancelableTask();
296  WithContext C(std::move(T.first));
297  S.runWithAST(ID, Path, [&, ID](llvm::Expected<InputsAndAST> E) {
298  if (auto Err = E.takeError()) {
299  if (Err.isA<CancelledError>()) {
300  ReadsCanceled.push_back(ID);
301  consumeError(std::move(Err));
302  } else {
303  ADD_FAILURE() << "Non-cancelled error for " << ID << ": "
304  << llvm::toString(std::move(Err));
305  }
306  } else {
307  ReadsSeen.push_back(ID);
308  }
309  });
310  return std::move(T.second);
311  };
312 
313  updateWithCallback(S, Path, "", WantDiagnostics::Yes,
314  [&]() { Proceed.wait(); });
315  // The second parens indicate cancellation, where present.
316  Update("U1")();
317  Read("R1")();
318  Update("U2")();
319  Read("R2A")();
320  Read("R2B");
321  Update("U3");
322  Read("R3")();
323  Proceed.notify();
324 
325  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
326  }
327  EXPECT_THAT(DiagsSeen, ElementsAre("U2", "U3"))
328  << "U1 and all dependent reads were cancelled. "
329  "U2 has a dependent read R2A. "
330  "U3 was not cancelled.";
331  EXPECT_THAT(ReadsSeen, ElementsAre("R2B"))
332  << "All reads other than R2B were cancelled";
333  EXPECT_THAT(ReadsCanceled, ElementsAre("R1", "R2A", "R3"))
334  << "All reads other than R2B were cancelled";
335 }
336 
337 TEST_F(TUSchedulerTests, InvalidationNoCrash) {
338  auto Path = testPath("foo.cpp");
339  TUScheduler S(CDB, optsForTest(), captureDiags());
340 
341  Notification StartedRunning;
342  Notification ScheduledChange;
343  // We expect invalidation logic to not crash by trying to invalidate a running
344  // request.
345  S.update(Path, getInputs(Path, ""), WantDiagnostics::Auto);
346  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
347  S.runWithAST(
348  "invalidatable-but-running", Path,
349  [&](llvm::Expected<InputsAndAST> AST) {
350  StartedRunning.notify();
351  ScheduledChange.wait();
352  ASSERT_TRUE(bool(AST));
353  },
355  StartedRunning.wait();
356  S.update(Path, getInputs(Path, ""), WantDiagnostics::Auto);
357  ScheduledChange.notify();
358  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
359 }
360 
361 TEST_F(TUSchedulerTests, Invalidation) {
362  auto Path = testPath("foo.cpp");
363  TUScheduler S(CDB, optsForTest(), captureDiags());
364  std::atomic<int> Builds(0), Actions(0);
365 
366  Notification Start;
367  updateWithDiags(S, Path, "a", WantDiagnostics::Yes, [&](std::vector<Diag>) {
368  ++Builds;
369  Start.wait();
370  });
371  S.runWithAST(
372  "invalidatable", Path,
373  [&](llvm::Expected<InputsAndAST> AST) {
374  ++Actions;
375  EXPECT_FALSE(bool(AST));
376  llvm::Error E = AST.takeError();
377  EXPECT_TRUE(E.isA<CancelledError>());
378  handleAllErrors(std::move(E), [&](const CancelledError &E) {
379  EXPECT_EQ(E.Reason, static_cast<int>(ErrorCode::ContentModified));
380  });
381  },
383  S.runWithAST(
384  "not-invalidatable", Path,
385  [&](llvm::Expected<InputsAndAST> AST) {
386  ++Actions;
387  EXPECT_TRUE(bool(AST));
388  },
390  updateWithDiags(S, Path, "b", WantDiagnostics::Auto, [&](std::vector<Diag>) {
391  ++Builds;
392  ADD_FAILURE() << "Shouldn't build, all dependents invalidated";
393  });
394  S.runWithAST(
395  "invalidatable", Path,
396  [&](llvm::Expected<InputsAndAST> AST) {
397  ++Actions;
398  EXPECT_FALSE(bool(AST));
399  llvm::Error E = AST.takeError();
400  EXPECT_TRUE(E.isA<CancelledError>());
401  consumeError(std::move(E));
402  },
404  updateWithDiags(S, Path, "c", WantDiagnostics::Auto,
405  [&](std::vector<Diag>) { ++Builds; });
406  S.runWithAST(
407  "invalidatable", Path,
408  [&](llvm::Expected<InputsAndAST> AST) {
409  ++Actions;
410  EXPECT_TRUE(bool(AST)) << "Shouldn't be invalidated, no update follows";
411  },
413  Start.notify();
414  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
415 
416  EXPECT_EQ(2, Builds.load()) << "Middle build should be skipped";
417  EXPECT_EQ(4, Actions.load()) << "All actions should run (some with error)";
418 }
419 
420 TEST_F(TUSchedulerTests, ManyUpdates) {
421  const int FilesCount = 3;
422  const int UpdatesPerFile = 10;
423 
424  std::mutex Mut;
425  int TotalASTReads = 0;
426  int TotalPreambleReads = 0;
427  int TotalUpdates = 0;
428  llvm::StringMap<int> LatestDiagVersion;
429 
430  // Run TUScheduler and collect some stats.
431  {
432  auto Opts = optsForTest();
433  Opts.UpdateDebounce = DebouncePolicy::fixed(std::chrono::milliseconds(50));
434  TUScheduler S(CDB, Opts, captureDiags());
435 
436  std::vector<std::string> Files;
437  for (int I = 0; I < FilesCount; ++I) {
438  std::string Name = "foo" + std::to_string(I) + ".cpp";
439  Files.push_back(testPath(Name));
440  this->FS.Files[Files.back()] = "";
441  }
442 
443  StringRef Contents1 = R"cpp(int a;)cpp";
444  StringRef Contents2 = R"cpp(int main() { return 1; })cpp";
445  StringRef Contents3 = R"cpp(int a; int b; int sum() { return a + b; })cpp";
446 
447  StringRef AllContents[] = {Contents1, Contents2, Contents3};
448  const int AllContentsSize = 3;
449 
450  // Scheduler may run tasks asynchronously, but should propagate the
451  // context. We stash a nonce in the context, and verify it in the task.
452  static Key<int> NonceKey;
453  int Nonce = 0;
454 
455  for (int FileI = 0; FileI < FilesCount; ++FileI) {
456  for (int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) {
457  auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize];
458 
459  auto File = Files[FileI];
460  auto Inputs = getInputs(File, Contents.str());
461  {
462  WithContextValue WithNonce(NonceKey, ++Nonce);
463  Inputs.Version = std::to_string(UpdateI);
464  updateWithDiags(
465  S, File, Inputs, WantDiagnostics::Auto,
466  [File, Nonce, Version(Inputs.Version), &Mut, &TotalUpdates,
467  &LatestDiagVersion](std::vector<Diag>) {
468  EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
469  EXPECT_EQ(File, boundPath());
470 
471  std::lock_guard<std::mutex> Lock(Mut);
472  ++TotalUpdates;
473  EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext());
474  // Make sure Diags are for a newer version.
475  auto It = LatestDiagVersion.try_emplace(File, -1);
476  const int PrevVersion = It.first->second;
477  int CurVersion;
478  ASSERT_TRUE(llvm::to_integer(Version, CurVersion, 10));
479  EXPECT_LT(PrevVersion, CurVersion);
480  It.first->getValue() = CurVersion;
481  });
482  }
483  {
484  WithContextValue WithNonce(NonceKey, ++Nonce);
485  S.runWithAST(
486  "CheckAST", File,
487  [File, Inputs, Nonce, &Mut,
488  &TotalASTReads](Expected<InputsAndAST> AST) {
489  EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
490  EXPECT_EQ(File, boundPath());
491 
492  ASSERT_TRUE((bool)AST);
493  EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
494  EXPECT_EQ(AST->Inputs.Version, Inputs.Version);
495  EXPECT_EQ(AST->AST.version(), Inputs.Version);
496 
497  std::lock_guard<std::mutex> Lock(Mut);
498  ++TotalASTReads;
500  });
501  }
502 
503  {
504  WithContextValue WithNonce(NonceKey, ++Nonce);
505  S.runWithPreamble(
506  "CheckPreamble", File, TUScheduler::Stale,
507  [File, Inputs, Nonce, &Mut,
508  &TotalPreambleReads](Expected<InputsAndPreamble> Preamble) {
509  EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
510  EXPECT_EQ(File, boundPath());
511 
512  ASSERT_TRUE((bool)Preamble);
513  EXPECT_EQ(Preamble->Contents, Inputs.Contents);
514 
515  std::lock_guard<std::mutex> Lock(Mut);
516  ++TotalPreambleReads;
518  });
519  }
520  }
521  }
522  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
523  } // TUScheduler destructor waits for all operations to finish.
524 
525  std::lock_guard<std::mutex> Lock(Mut);
526  // Updates might get coalesced in preamble thread and result in dropping
527  // diagnostics for intermediate snapshots.
528  EXPECT_GE(TotalUpdates, FilesCount);
529  EXPECT_LE(TotalUpdates, FilesCount * UpdatesPerFile);
530  // We should receive diags for last update.
531  for (const auto &Entry : LatestDiagVersion)
532  EXPECT_EQ(Entry.second, UpdatesPerFile - 1);
533  EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile);
534  EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile);
535 }
536 
537 TEST_F(TUSchedulerTests, EvictedAST) {
538  std::atomic<int> BuiltASTCounter(0);
539  auto Opts = optsForTest();
540  Opts.AsyncThreadsCount = 1;
541  Opts.RetentionPolicy.MaxRetainedASTs = 2;
542  trace::TestTracer Tracer;
543  TUScheduler S(CDB, Opts);
544 
545  llvm::StringLiteral SourceContents = R"cpp(
546  int* a;
547  double* b = a;
548  )cpp";
549  llvm::StringLiteral OtherSourceContents = R"cpp(
550  int* a;
551  double* b = a + 0;
552  )cpp";
553 
554  auto Foo = testPath("foo.cpp");
555  auto Bar = testPath("bar.cpp");
556  auto Baz = testPath("baz.cpp");
557 
558  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(0));
559  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(0));
560  // Build one file in advance. We will not access it later, so it will be the
561  // one that the cache will evict.
562  updateWithCallback(S, Foo, SourceContents, WantDiagnostics::Yes,
563  [&BuiltASTCounter]() { ++BuiltASTCounter; });
564  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
565  ASSERT_EQ(BuiltASTCounter.load(), 1);
566  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(0));
567  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(1));
568 
569  // Build two more files. Since we can retain only 2 ASTs, these should be
570  // the ones we see in the cache later.
571  updateWithCallback(S, Bar, SourceContents, WantDiagnostics::Yes,
572  [&BuiltASTCounter]() { ++BuiltASTCounter; });
573  updateWithCallback(S, Baz, SourceContents, WantDiagnostics::Yes,
574  [&BuiltASTCounter]() { ++BuiltASTCounter; });
575  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
576  ASSERT_EQ(BuiltASTCounter.load(), 3);
577  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(0));
578  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(2));
579 
580  // Check only the last two ASTs are retained.
581  ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(Bar, Baz));
582 
583  // Access the old file again.
584  updateWithCallback(S, Foo, OtherSourceContents, WantDiagnostics::Yes,
585  [&BuiltASTCounter]() { ++BuiltASTCounter; });
586  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
587  ASSERT_EQ(BuiltASTCounter.load(), 4);
588  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(0));
589  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(1));
590 
591  // Check the AST for foo.cpp is retained now and one of the others got
592  // evicted.
593  EXPECT_THAT(S.getFilesWithCachedAST(),
594  UnorderedElementsAre(Foo, AnyOf(Bar, Baz)));
595 }
596 
597 // We send "empty" changes to TUScheduler when we think some external event
598 // *might* have invalidated current state (e.g. a header was edited).
599 // Verify that this doesn't evict our cache entries.
600 TEST_F(TUSchedulerTests, NoopChangesDontThrashCache) {
601  auto Opts = optsForTest();
602  Opts.RetentionPolicy.MaxRetainedASTs = 1;
603  TUScheduler S(CDB, Opts);
604 
605  auto Foo = testPath("foo.cpp");
606  auto FooInputs = getInputs(Foo, "int x=1;");
607  auto Bar = testPath("bar.cpp");
608  auto BarInputs = getInputs(Bar, "int x=2;");
609 
610  // After opening Foo then Bar, AST cache contains Bar.
611  S.update(Foo, FooInputs, WantDiagnostics::Auto);
612  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
613  S.update(Bar, BarInputs, WantDiagnostics::Auto);
614  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
615  ASSERT_THAT(S.getFilesWithCachedAST(), ElementsAre(Bar));
616 
617  // Any number of no-op updates to Foo don't dislodge Bar from the cache.
618  S.update(Foo, FooInputs, WantDiagnostics::Auto);
619  S.update(Foo, FooInputs, WantDiagnostics::Auto);
620  S.update(Foo, FooInputs, WantDiagnostics::Auto);
621  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
622  ASSERT_THAT(S.getFilesWithCachedAST(), ElementsAre(Bar));
623  // In fact each file has been built only once.
624  ASSERT_EQ(S.fileStats().lookup(Foo).ASTBuilds, 1u);
625  ASSERT_EQ(S.fileStats().lookup(Bar).ASTBuilds, 1u);
626 }
627 
628 TEST_F(TUSchedulerTests, EmptyPreamble) {
629  TUScheduler S(CDB, optsForTest());
630 
631  auto Foo = testPath("foo.cpp");
632  auto Header = testPath("foo.h");
633 
634  FS.Files[Header] = "void foo()";
635  FS.Timestamps[Header] = time_t(0);
636  auto WithPreamble = R"cpp(
637  #include "foo.h"
638  int main() {}
639  )cpp";
640  auto WithEmptyPreamble = R"cpp(int main() {})cpp";
641  S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto);
642  S.runWithPreamble(
643  "getNonEmptyPreamble", Foo, TUScheduler::Stale,
644  [&](Expected<InputsAndPreamble> Preamble) {
645  // We expect to get a non-empty preamble.
646  EXPECT_GT(
647  cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
648  0u);
649  });
650  // Wait for the preamble is being built.
651  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
652 
653  // Update the file which results in an empty preamble.
654  S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto);
655  // Wait for the preamble is being built.
656  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
657  S.runWithPreamble(
658  "getEmptyPreamble", Foo, TUScheduler::Stale,
659  [&](Expected<InputsAndPreamble> Preamble) {
660  // We expect to get an empty preamble.
661  EXPECT_EQ(
662  cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
663  0u);
664  });
665 }
666 
667 TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
668  // Testing strategy: we update the file and schedule a few preamble reads at
669  // the same time. All reads should get the same non-null preamble.
670  TUScheduler S(CDB, optsForTest());
671  auto Foo = testPath("foo.cpp");
672  auto NonEmptyPreamble = R"cpp(
673  #define FOO 1
674  #define BAR 2
675 
676  int main() {}
677  )cpp";
678  constexpr int ReadsToSchedule = 10;
679  std::mutex PreamblesMut;
680  std::vector<const void *> Preambles(ReadsToSchedule, nullptr);
681  S.update(Foo, getInputs(Foo, NonEmptyPreamble), WantDiagnostics::Auto);
682  for (int I = 0; I < ReadsToSchedule; ++I) {
683  S.runWithPreamble(
684  "test", Foo, TUScheduler::Stale,
685  [I, &PreamblesMut, &Preambles](Expected<InputsAndPreamble> IP) {
686  std::lock_guard<std::mutex> Lock(PreamblesMut);
687  Preambles[I] = cantFail(std::move(IP)).Preamble;
688  });
689  }
690  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
691  // Check all actions got the same non-null preamble.
692  std::lock_guard<std::mutex> Lock(PreamblesMut);
693  ASSERT_NE(Preambles[0], nullptr);
694  ASSERT_THAT(Preambles, Each(Preambles[0]));
695 }
696 
697 TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
698  TUScheduler S(CDB, optsForTest(), captureDiags());
699 
700  auto Source = testPath("foo.cpp");
701  auto Header = testPath("foo.h");
702 
703  FS.Files[Header] = "int a;";
704  FS.Timestamps[Header] = time_t(0);
705 
706  std::string SourceContents = R"cpp(
707  #include "foo.h"
708  int b = a;
709  )cpp";
710 
711  // Return value indicates if the updated callback was received.
712  auto DoUpdate = [&](std::string Contents) -> bool {
713  std::atomic<bool> Updated(false);
714  Updated = false;
715  updateWithDiags(S, Source, Contents, WantDiagnostics::Yes,
716  [&Updated](std::vector<Diag>) { Updated = true; });
717  bool UpdateFinished = S.blockUntilIdle(timeoutSeconds(10));
718  if (!UpdateFinished)
719  ADD_FAILURE() << "Updated has not finished in one second. Threading bug?";
720  return Updated;
721  };
722 
723  // Test that subsequent updates with the same inputs do not cause rebuilds.
724  ASSERT_TRUE(DoUpdate(SourceContents));
725  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 1u);
726  ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 1u);
727  ASSERT_FALSE(DoUpdate(SourceContents));
728  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 1u);
729  ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 1u);
730 
731  // Update to a header should cause a rebuild, though.
732  FS.Timestamps[Header] = time_t(1);
733  ASSERT_TRUE(DoUpdate(SourceContents));
734  ASSERT_FALSE(DoUpdate(SourceContents));
735  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 2u);
736  ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 2u);
737 
738  // Update to the contents should cause a rebuild.
739  SourceContents += "\nint c = b;";
740  ASSERT_TRUE(DoUpdate(SourceContents));
741  ASSERT_FALSE(DoUpdate(SourceContents));
742  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 3u);
743  ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 2u);
744 
745  // Update to the compile commands should also cause a rebuild.
746  CDB.ExtraClangFlags.push_back("-DSOMETHING");
747  ASSERT_TRUE(DoUpdate(SourceContents));
748  ASSERT_FALSE(DoUpdate(SourceContents));
749  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 4u);
750  ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 3u);
751 }
752 
753 // We rebuild if a completely missing header exists, but not if one is added
754 // on a higher-priority include path entry (for performance).
755 // (Previously we wouldn't automatically rebuild when files were added).
756 TEST_F(TUSchedulerTests, MissingHeader) {
757  CDB.ExtraClangFlags.push_back("-I" + testPath("a"));
758  CDB.ExtraClangFlags.push_back("-I" + testPath("b"));
759  // Force both directories to exist so they don't get pruned.
760  FS.Files.try_emplace("a/__unused__");
761  FS.Files.try_emplace("b/__unused__");
762  TUScheduler S(CDB, optsForTest(), captureDiags());
763 
764  auto Source = testPath("foo.cpp");
765  auto HeaderA = testPath("a/foo.h");
766  auto HeaderB = testPath("b/foo.h");
767 
768  auto SourceContents = R"cpp(
769  #include "foo.h"
770  int c = b;
771  )cpp";
772 
773  ParseInputs Inputs = getInputs(Source, SourceContents);
774  std::atomic<size_t> DiagCount(0);
775 
776  // Update the source contents, which should trigger an initial build with
777  // the header file missing.
778  updateWithDiags(
779  S, Source, Inputs, WantDiagnostics::Yes,
780  [&DiagCount](std::vector<Diag> Diags) {
781  ++DiagCount;
782  EXPECT_THAT(Diags,
783  ElementsAre(Field(&Diag::Message, "'foo.h' file not found"),
785  "use of undeclared identifier 'b'")));
786  });
787  S.blockUntilIdle(timeoutSeconds(10));
788 
789  FS.Files[HeaderB] = "int b;";
790  FS.Timestamps[HeaderB] = time_t(1);
791 
792  // The addition of the missing header file triggers a rebuild, no errors.
793  updateWithDiags(S, Source, Inputs, WantDiagnostics::Yes,
794  [&DiagCount](std::vector<Diag> Diags) {
795  ++DiagCount;
796  EXPECT_THAT(Diags, IsEmpty());
797  });
798 
799  // Ensure previous assertions are done before we touch the FS again.
800  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
801  // Add the high-priority header file, which should reintroduce the error.
802  FS.Files[HeaderA] = "int a;";
803  FS.Timestamps[HeaderA] = time_t(1);
804 
805  // This isn't detected: we don't stat a/foo.h to validate the preamble.
806  updateWithDiags(S, Source, Inputs, WantDiagnostics::Yes,
807  [&DiagCount](std::vector<Diag> Diags) {
808  ++DiagCount;
809  ADD_FAILURE()
810  << "Didn't expect new diagnostics when adding a/foo.h";
811  });
812 
813  // Forcing the reload should should cause a rebuild.
814  Inputs.ForceRebuild = true;
815  updateWithDiags(
816  S, Source, Inputs, WantDiagnostics::Yes,
817  [&DiagCount](std::vector<Diag> Diags) {
818  ++DiagCount;
819  ElementsAre(Field(&Diag::Message, "use of undeclared identifier 'b'"));
820  });
821 
822  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
823  EXPECT_EQ(DiagCount, 3U);
824 }
825 
826 TEST_F(TUSchedulerTests, NoChangeDiags) {
827  trace::TestTracer Tracer;
828  TUScheduler S(CDB, optsForTest(), captureDiags());
829 
830  auto FooCpp = testPath("foo.cpp");
831  const auto *Contents = "int a; int b;";
832 
833  EXPECT_THAT(Tracer.takeMetric("ast_access_read", "hit"), SizeIs(0));
834  EXPECT_THAT(Tracer.takeMetric("ast_access_read", "miss"), SizeIs(0));
835  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(0));
836  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(0));
837  updateWithDiags(
838  S, FooCpp, Contents, WantDiagnostics::No,
839  [](std::vector<Diag>) { ADD_FAILURE() << "Should not be called."; });
840  S.runWithAST("touchAST", FooCpp, [](Expected<InputsAndAST> IA) {
841  // Make sure the AST was actually built.
842  cantFail(std::move(IA));
843  });
844  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
845  EXPECT_THAT(Tracer.takeMetric("ast_access_read", "hit"), SizeIs(0));
846  EXPECT_THAT(Tracer.takeMetric("ast_access_read", "miss"), SizeIs(1));
847 
848  // Even though the inputs didn't change and AST can be reused, we need to
849  // report the diagnostics, as they were not reported previously.
850  std::atomic<bool> SeenDiags(false);
851  updateWithDiags(S, FooCpp, Contents, WantDiagnostics::Auto,
852  [&](std::vector<Diag>) { SeenDiags = true; });
853  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
854  ASSERT_TRUE(SeenDiags);
855  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "hit"), SizeIs(1));
856  EXPECT_THAT(Tracer.takeMetric("ast_access_diag", "miss"), SizeIs(0));
857 
858  // Subsequent request does not get any diagnostics callback because the same
859  // diags have previously been reported and the inputs didn't change.
860  updateWithDiags(
861  S, FooCpp, Contents, WantDiagnostics::Auto,
862  [&](std::vector<Diag>) { ADD_FAILURE() << "Should not be called."; });
863  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
864 }
865 
866 TEST_F(TUSchedulerTests, Run) {
867  auto Opts = optsForTest();
868  Opts.ContextProvider = bindPath;
869  TUScheduler S(CDB, Opts);
870  std::atomic<int> Counter(0);
871  S.run("add 1", /*Path=*/"", [&] { ++Counter; });
872  S.run("add 2", /*Path=*/"", [&] { Counter += 2; });
873  ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
874  EXPECT_EQ(Counter.load(), 3);
875 
876  Notification TaskRun;
877  Key<int> TestKey;
878  WithContextValue CtxWithKey(TestKey, 10);
879  const char *Path = "somepath";
880  S.run("props context", Path, [&] {
881  EXPECT_EQ(Context::current().getExisting(TestKey), 10);
882  EXPECT_EQ(Path, boundPath());
883  TaskRun.notify();
884  });
885  TaskRun.wait();
886 }
887 
888 TEST_F(TUSchedulerTests, TUStatus) {
889  class CaptureTUStatus : public ClangdServer::Callbacks {
890  public:
891  void onFileUpdated(PathRef File, const TUStatus &Status) override {
892  auto ASTAction = Status.ASTActivity.K;
893  auto PreambleAction = Status.PreambleActivity;
894  std::lock_guard<std::mutex> Lock(Mutex);
895  // Only push the action if it has changed. Since TUStatus can be published
896  // from either Preamble or AST thread and when one changes the other stays
897  // the same.
898  // Note that this can result in missing some updates when something other
899  // than action kind changes, e.g. when AST is built/reused the action kind
900  // stays as Building.
901  if (ASTActions.empty() || ASTActions.back() != ASTAction)
902  ASTActions.push_back(ASTAction);
903  if (PreambleActions.empty() || PreambleActions.back() != PreambleAction)
904  PreambleActions.push_back(PreambleAction);
905  }
906 
907  std::vector<PreambleAction> preambleStatuses() {
908  std::lock_guard<std::mutex> Lock(Mutex);
909  return PreambleActions;
910  }
911 
912  std::vector<ASTAction::Kind> astStatuses() {
913  std::lock_guard<std::mutex> Lock(Mutex);
914  return ASTActions;
915  }
916 
917  private:
918  std::mutex Mutex;
919  std::vector<ASTAction::Kind> ASTActions;
920  std::vector<PreambleAction> PreambleActions;
921  } CaptureTUStatus;
922  MockFS FS;
923  MockCompilationDatabase CDB;
924  ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &CaptureTUStatus);
925  Annotations Code("int m^ain () {}");
926 
927  // We schedule the following tasks in the queue:
928  // [Update] [GoToDefinition]
929  Server.addDocument(testPath("foo.cpp"), Code.code(), "1",
931  ASSERT_TRUE(Server.blockUntilIdleForTest());
932  Server.locateSymbolAt(testPath("foo.cpp"), Code.point(),
933  [](Expected<std::vector<LocatedSymbol>> Result) {
934  ASSERT_TRUE((bool)Result);
935  });
936  ASSERT_TRUE(Server.blockUntilIdleForTest());
937 
938  EXPECT_THAT(CaptureTUStatus.preambleStatuses(),
939  ElementsAre(
940  // PreambleThread starts idle, as the update is first handled
941  // by ASTWorker.
943  // Then it starts building first preamble and releases that to
944  // ASTWorker.
946  // Then goes idle and stays that way as we don't receive any
947  // more update requests.
949  EXPECT_THAT(CaptureTUStatus.astStatuses(),
950  ElementsAre(
951  // Starts handling the update action and blocks until the
952  // first preamble is built.
954  // Afterwqards it builds an AST for that preamble to publish
955  // diagnostics.
957  // Then goes idle.
959  // Afterwards we start executing go-to-def.
961  // Then go idle.
962  ASTAction::Idle));
963 }
964 
965 TEST_F(TUSchedulerTests, CommandLineErrors) {
966  // We should see errors from command-line parsing inside the main file.
967  CDB.ExtraClangFlags = {"-fsome-unknown-flag"};
968 
969  // (!) 'Ready' must live longer than TUScheduler.
970  Notification Ready;
971 
972  TUScheduler S(CDB, optsForTest(), captureDiags());
973  std::vector<Diag> Diagnostics;
974  updateWithDiags(S, testPath("foo.cpp"), "void test() {}",
975  WantDiagnostics::Yes, [&](std::vector<Diag> D) {
976  Diagnostics = std::move(D);
977  Ready.notify();
978  });
979  Ready.wait();
980 
981  EXPECT_THAT(
982  Diagnostics,
983  ElementsAre(AllOf(
984  Field(&Diag::ID, Eq(diag::err_drv_unknown_argument)),
985  Field(&Diag::Name, Eq("drv_unknown_argument")),
986  Field(&Diag::Message, "unknown argument: '-fsome-unknown-flag'"))));
987 }
988 
989 TEST_F(TUSchedulerTests, CommandLineWarnings) {
990  // We should not see warnings from command-line parsing.
991  CDB.ExtraClangFlags = {"-Wsome-unknown-warning"};
992 
993  // (!) 'Ready' must live longer than TUScheduler.
994  Notification Ready;
995 
996  TUScheduler S(CDB, optsForTest(), captureDiags());
997  std::vector<Diag> Diagnostics;
998  updateWithDiags(S, testPath("foo.cpp"), "void test() {}",
999  WantDiagnostics::Yes, [&](std::vector<Diag> D) {
1000  Diagnostics = std::move(D);
1001  Ready.notify();
1002  });
1003  Ready.wait();
1004 
1005  EXPECT_THAT(Diagnostics, IsEmpty());
1006 }
1007 
1008 TEST(DebouncePolicy, Compute) {
1009  namespace c = std::chrono;
1010  std::vector<DebouncePolicy::clock::duration> History = {
1011  c::seconds(0),
1012  c::seconds(5),
1013  c::seconds(10),
1014  c::seconds(20),
1015  };
1016  DebouncePolicy Policy;
1017  Policy.Min = c::seconds(3);
1018  Policy.Max = c::seconds(25);
1019  // Call Policy.compute(History) and return seconds as a float.
1020  auto Compute = [&](llvm::ArrayRef<DebouncePolicy::clock::duration> History) {
1021  using FloatingSeconds = c::duration<float, c::seconds::period>;
1022  return static_cast<float>(Policy.compute(History) / FloatingSeconds(1));
1023  };
1024  EXPECT_NEAR(10, Compute(History), 0.01) << "(upper) median = 10";
1025  Policy.RebuildRatio = 1.5;
1026  EXPECT_NEAR(15, Compute(History), 0.01) << "median = 10, ratio = 1.5";
1027  Policy.RebuildRatio = 3;
1028  EXPECT_NEAR(25, Compute(History), 0.01) << "constrained by max";
1029  Policy.RebuildRatio = 0;
1030  EXPECT_NEAR(3, Compute(History), 0.01) << "constrained by min";
1031  EXPECT_NEAR(25, Compute({}), 0.01) << "no history -> max";
1032 }
1033 
1034 TEST_F(TUSchedulerTests, AsyncPreambleThread) {
1035  // Blocks preamble thread while building preamble with \p BlockVersion until
1036  // \p N is notified.
1037  class BlockPreambleThread : public ParsingCallbacks {
1038  public:
1039  BlockPreambleThread(llvm::StringRef BlockVersion, Notification &N)
1040  : BlockVersion(BlockVersion), N(N) {}
1041  void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx,
1042  std::shared_ptr<clang::Preprocessor> PP,
1043  const CanonicalIncludes &) override {
1044  if (Version == BlockVersion)
1045  N.wait();
1046  }
1047 
1048  private:
1049  llvm::StringRef BlockVersion;
1050  Notification &N;
1051  };
1052 
1053  static constexpr llvm::StringLiteral InputsV0 = "v0";
1054  static constexpr llvm::StringLiteral InputsV1 = "v1";
1055  Notification Ready;
1056  TUScheduler S(CDB, optsForTest(),
1057  std::make_unique<BlockPreambleThread>(InputsV1, Ready));
1058 
1059  Path File = testPath("foo.cpp");
1060  auto PI = getInputs(File, "");
1061  PI.Version = InputsV0.str();
1062  S.update(File, PI, WantDiagnostics::Auto);
1063  S.blockUntilIdle(timeoutSeconds(10));
1064 
1065  // Block preamble builds.
1066  PI.Version = InputsV1.str();
1067  // Issue second update which will block preamble thread.
1068  S.update(File, PI, WantDiagnostics::Auto);
1069 
1070  Notification RunASTAction;
1071  // Issue an AST read, which shouldn't be blocked and see latest version of the
1072  // file.
1073  S.runWithAST("test", File, [&](Expected<InputsAndAST> AST) {
1074  ASSERT_TRUE(bool(AST));
1075  // Make sure preamble is built with stale inputs, but AST was built using
1076  // new ones.
1077  EXPECT_THAT(AST->AST.preambleVersion(), InputsV0);
1078  EXPECT_THAT(AST->Inputs.Version, InputsV1.str());
1079  RunASTAction.notify();
1080  });
1081  RunASTAction.wait();
1082  Ready.notify();
1083 }
1084 
1085 } // namespace
1086 } // namespace clangd
1087 } // namespace clang
clang::clangd::ParseInputs::Version
std::string Version
Definition: Compiler.h:52
clang::clangd::cancelableTask
std::pair< Context, Canceler > cancelableTask(int Reason)
Defines a new task whose cancellation may be requested.
Definition: Cancellation.cpp:24
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:704
clang::clangd::timeoutSeconds
Deadline timeoutSeconds(llvm::Optional< double > Seconds)
Makes a deadline from a timeout in seconds. None means wait forever.
Definition: Threading.cpp:102
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::clangd::ParseInputs::ForceRebuild
bool ForceRebuild
Definition: Compiler.h:55
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
DiagsCallbackKey
static Key< llvm::unique_function< void(PathRef File, std::vector< Diag >)> > DiagsCallbackKey
Definition: TUSchedulerTests.cpp:110
clang::clangd::ParseInputs::Contents
std::string Contents
Definition: Compiler.h:50
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
Tracer
std::unique_ptr< trace::EventTracer > Tracer
Definition: TraceTests.cpp:163
Path.h
clang::clangd::Diag::Name
std::string Name
Definition: Diagnostics.h:87
clang::clangd::Context::current
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
clang::clangd::WantDiagnostics::Yes
clang::clangd::Context::get
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
Definition: Context.h:100
clang::clangd::CompletionItemKind::Field
clang::clangd::WantDiagnostics::Auto
Diagnostics must not be generated for this snapshot.
clang::clangd::PreambleData::Preamble
PrecompiledPreamble Preamble
Definition: Preamble.h:59
clang::tidy::utils::fixit::QualifierTarget::Pointee
Contents
llvm::StringRef Contents
Definition: DraftStoreTests.cpp:22
clang::clangd::PreambleAction
PreambleAction
Definition: TUScheduler.h:86
Preamble.h
Ctx
Context Ctx
Definition: TUScheduler.cpp:324
clang::clangd::ClangdServer::optsForTest
static Options optsForTest()
Definition: ClangdServer.cpp:114
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:92
FS
MockFS FS
Definition: TUSchedulerTests.cpp:165
clang::clangd::TUScheduler::Stale
The preamble may be generated from an older version of the file.
Definition: TUScheduler.h:277
Preamble
const PreambleData & Preamble
Definition: CodeComplete.cpp:1045
Cancellation.h
clang::clangd::WantDiagnostics
WantDiagnostics
Determines whether diagnostics should be generated for a file snapshot.
Definition: TUScheduler.h:48
ThreadsafeFS.h
Inputs
ParseInputs Inputs
Definition: TUScheduler.cpp:321
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::ParsedAST::version
llvm::StringRef version() const
Returns the version of the ParseInputs this AST was built from.
Definition: ParsedAST.h:106
clang::clangd::DiagBase::Message
std::string Message
Definition: Diagnostics.h:55
clang::clangd::PreambleAction::Idle
clang::clangd::SymbolKind::Key
clang::clangd::MockFS::Timestamps
llvm::StringMap< time_t > Timestamps
Definition: TestFS.h:42
TestFS.h
Threading.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
Diagnostics.h
clang::clangd::ASTAction::Idle
Definition: TUScheduler.h:96
clang::clangd::SymbolOrigin::AST
EXPECT_ERROR
#define EXPECT_ERROR(expectedValue)
Definition: clangd/unittests/Matchers.h:118
Annotations.h
clang::clangd::ASTAction::RunningAction
Definition: TUScheduler.h:94
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
TestTracer.h
Entry
Definition: Modularize.cpp:429
clang::clangd::CompletionItemKind::File
Counter
trace::Metric Counter
Definition: TraceTests.cpp:151
clang::clangd::Context::derive
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
Definition: Context.h:121
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
clang::clangd::Canceler
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
Definition: Cancellation.h:70
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::TUScheduler::NoInvalidation
The request will run unless explicitly cancelled.
Definition: TUScheduler.h:248
ClangdServer.h
TUScheduler.h
clang::clangd::DiagBase::ID
unsigned ID
Definition: Diagnostics.h:68
CDB
MockCompilationDatabase CDB
Definition: TUSchedulerTests.cpp:166
Diags
CapturedDiags Diags
Definition: ConfigCompileTests.cpp:26
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:60
clang::clangd::ASTAction::Building
Definition: TUScheduler.h:95
clang::clangd::MockFS::Files
llvm::StringMap< std::string > Files
Definition: TestFS.h:41
clang::clangd::DebouncePolicy::fixed
static DebouncePolicy fixed(clock::duration)
A policy that always returns the same duration, useful for tests.
Definition: TUScheduler.cpp:1423
clang::clangd::CompletionItemKind::Missing
clang::clangd::DocumentHighlightKind::Read
clang::clangd::PreambleAction::Building
clang::clangd::WantDiagnostics::No
Diagnostics must be generated for this snapshot.
clang::clangd::MockCompilationDatabase::ExtraClangFlags
std::vector< std::string > ExtraClangFlags
Definition: TestFS.h:61
Matchers.h
clang::clangd::ErrorCode::ContentModified
clang::clangd::MessageType::Error
An error message.
clang::clangd::ParsedAST::preambleVersion
llvm::Optional< llvm::StringRef > preambleVersion() const
Returns the version of the ParseInputs used to build Preamble part of this AST.
Definition: ParsedAST.cpp:552
Context.h
clang::clangd::TUScheduler::getFileBeingProcessedInContext
static llvm::Optional< llvm::StringRef > getFileBeingProcessedInContext()
Definition: TUScheduler.cpp:100
ParsedAST.h
clang::clangd::TUScheduler::InvalidateOnUpdate
The request will be implicitly cancelled by a subsequent update().
Definition: TUScheduler.h:253