ioeric created this revision.
ioeric added a reviewer: ilya-biryukov.
Herald added subscribers: cfe-commits, jdoerfert, kadircet, arphaman, jkorous, 
MaskRay, javed.absar.
Herald added a project: clang.

When calling TUScehduler::runWithPreamble (e.g. in code compleiton), allow
entering a fallback mode when compile command or preamble is not ready, instead 
of
waiting. This allows clangd to perform naive code completion e.g. using 
identifiers
in the current file or symbols in the index.

This patch simply returns empty result for code completion in fallback mode. 
Identifier-based
plus more advanced index-based completion will be added in followup patches.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D59811

Files:
  clangd/ClangdServer.cpp
  clangd/TUScheduler.cpp
  clangd/TUScheduler.h
  clangd/Threading.h
  unittests/clangd/ClangdTests.cpp
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/TUSchedulerTests.cpp

Index: unittests/clangd/TUSchedulerTests.cpp
===================================================================
--- unittests/clangd/TUSchedulerTests.cpp
+++ unittests/clangd/TUSchedulerTests.cpp
@@ -15,14 +15,13 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <algorithm>
+#include <thread>
 #include <utility>
 
 namespace clang {
 namespace clangd {
 namespace {
 
-using ::testing::_;
-using ::testing::AllOf;
 using ::testing::AnyOf;
 using ::testing::Each;
 using ::testing::ElementsAre;
@@ -35,13 +34,18 @@
 
 class TUSchedulerTests : public ::testing::Test {
 protected:
-  ParseInputs getInputs(PathRef File, std::string Contents) {
+  FileUpdateInputs getInputs(PathRef File, std::string Contents) {
     ParseInputs Inputs;
-    Inputs.CompileCommand = *CDB.getCompileCommand(File);
     Inputs.FS = buildTestFS(Files, Timestamps);
     Inputs.Contents = std::move(Contents);
     Inputs.Opts = ParseOptions();
-    return Inputs;
+    FileUpdateInputs UpdateInputs;
+    UpdateInputs.InputSansCommand = std::move(Inputs);
+    std::string FilePath = File;
+    UpdateInputs.GetCompileCommand = [this, FilePath]() {
+      return *CDB.getCompileCommand(FilePath);
+    };
+    return UpdateInputs;
   }
 
   void updateWithCallback(TUScheduler &S, PathRef File,
@@ -72,7 +76,7 @@
   /// Schedule an update and call \p CB with the diagnostics it produces, if
   /// any. The TUScheduler should be created with captureDiags as a
   /// DiagsCallback for this to work.
-  void updateWithDiags(TUScheduler &S, PathRef File, ParseInputs Inputs,
+  void updateWithDiags(TUScheduler &S, PathRef File, FileUpdateInputs Inputs,
                        WantDiagnostics WD,
                        llvm::unique_function<void(std::vector<Diag>)> CB) {
     Path OrigFile = File.str();
@@ -245,7 +249,7 @@
 
     S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,
                       [&](Expected<InputsAndPreamble> Pre) {
-                        ASSERT_TRUE(bool(Pre));
+                        EXPECT_TRUE((bool)Pre);
                         assert(bool(Pre));
                         EXPECT_THAT(includes(Pre->Preamble),
                                     ElementsAre("<A>"));
@@ -397,8 +401,9 @@
                 EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
 
                 ASSERT_TRUE((bool)AST);
-                EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
-                EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
+                EXPECT_EQ(AST->Inputs.FS, Inputs.InputSansCommand.FS);
+                EXPECT_EQ(AST->Inputs.Contents,
+                          Inputs.InputSansCommand.Contents);
 
                 std::lock_guard<std::mutex> Lock(Mut);
                 ++TotalASTReads;
@@ -415,7 +420,7 @@
                 EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
 
                 ASSERT_TRUE((bool)Preamble);
-                EXPECT_EQ(Preamble->Contents, Inputs.Contents);
+                EXPECT_EQ(Preamble->Contents, Inputs.InputSansCommand.Contents);
 
                 std::lock_guard<std::mutex> Lock(Mut);
                 ++TotalPreambleReads;
@@ -504,6 +509,7 @@
   )cpp";
   auto WithEmptyPreamble = R"cpp(int main() {})cpp";
   S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto);
+
   S.runWithPreamble(
       "getNonEmptyPreamble", Foo, TUScheduler::Stale,
       [&](Expected<InputsAndPreamble> Preamble) {
Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -1358,6 +1358,7 @@
   // parsing.
   CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing");
   Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
+  ASSERT_TRUE(Server.blockUntilIdleForTest());
   CodeCompleteResult Completions = cantFail(runCodeComplete(
       Server, FooCpp, Source.point(), clangd::CodeCompleteOptions()));
 
Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -13,6 +13,7 @@
 #include "Matchers.h"
 #include "SyncAPI.h"
 #include "TestFS.h"
+#include "Threading.h"
 #include "URI.h"
 #include "clang/Config/config.h"
 #include "llvm/ADT/SmallVector.h"
@@ -36,7 +37,6 @@
 namespace {
 
 using ::testing::ElementsAre;
-using ::testing::Eq;
 using ::testing::Field;
 using ::testing::Gt;
 using ::testing::IsEmpty;
@@ -1058,6 +1058,54 @@
   EXPECT_NE(Result, "<no-ast>");
 }
 
+TEST_F(ClangdVFSTest, FallbackWhenPreambleIsNotReady) {
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  // Returns compile command only when notified.
+  class DelayedCompilationDatabase : public GlobalCompilationDatabase {
+  public:
+    DelayedCompilationDatabase(Notification &CanReturnCommand)
+        : CanReturnCommand(CanReturnCommand) {}
+
+    llvm::Optional<tooling::CompileCommand>
+    getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override {
+      CanReturnCommand.wait();
+      auto FileName = llvm::sys::path::filename(File);
+      std::vector<std::string> CommandLine = {"clangd", "-ffreestanding", File};
+      return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
+                                      FileName, std::move(CommandLine), "")};
+    }
+
+    std::vector<std::string> ExtraClangFlags;
+
+  private:
+    Notification &CanReturnCommand;
+  };
+
+  Notification CanReturnCommand;
+  DelayedCompilationDatabase CDB(CanReturnCommand);
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  auto FooCpp = testPath("foo.cpp");
+  Annotations Code(R"cpp(
+    int main() {
+      int xyz;
+      xy^
+    })cpp");
+  FS.Files[FooCpp] = FooCpp;
+  Server.addDocument(FooCpp, Code.code());
+  EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
+                                       clangd::CodeCompleteOptions()))
+                  .Completions,
+              IsEmpty());
+  CanReturnCommand.notify();
+  ASSERT_TRUE(Server.blockUntilIdleForTest());
+  EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, Code.point(),
+                                       clangd::CodeCompleteOptions()))
+                  .Completions,
+              ElementsAre(Field(&CodeCompletion::Name, "xyz")));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/Threading.h
===================================================================
--- clangd/Threading.h
+++ clangd/Threading.h
@@ -27,6 +27,7 @@
 public:
   // Sets the flag. No-op if already set.
   void notify();
+  bool notified() const { return Notified; }
   // Blocks until flag is set.
   void wait() const;
 
Index: clangd/TUScheduler.h
===================================================================
--- clangd/TUScheduler.h
+++ clangd/TUScheduler.h
@@ -13,7 +13,9 @@
 #include "Function.h"
 #include "Threading.h"
 #include "index/CanonicalIncludes.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
 #include <future>
 
 namespace clang {
@@ -24,6 +26,13 @@
 /// synchronously).
 unsigned getDefaultAsyncThreadsCount();
 
+// Inputs for updating a file in TUScheduler.
+struct FileUpdateInputs {
+  std::function<tooling::CompileCommand()> GetCompileCommand = nullptr;
+  // Contains all other inputs except for compile command.
+  ParseInputs InputSansCommand;
+};
+
 struct InputsAndAST {
   const ParseInputs &Inputs;
   ParsedAST &AST;
@@ -31,7 +40,9 @@
 
 struct InputsAndPreamble {
   llvm::StringRef Contents;
-  const tooling::CompileCommand &Command;
+  // Can be None in fallback mode when neither Command nor Preamble is ready.
+  llvm::Optional<tooling::CompileCommand> Command;
+  // In fallback mode, this can be nullptr when preamble is not ready.
   const PreambleData *Preamble;
 };
 
@@ -142,7 +153,7 @@
   /// If diagnostics are requested (Yes), and the context is cancelled before
   /// they are prepared, they may be skipped if eventual-consistency permits it
   /// (i.e. WantDiagnostics is downgraded to Auto).
-  void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD);
+  void update(PathRef File, FileUpdateInputs UpdateInputs, WantDiagnostics WD);
 
   /// Remove \p File from the list of tracked files and schedule removal of its
   /// resources. Pending diagnostics for closed files may not be delivered, even
@@ -179,15 +190,18 @@
     /// This is the fastest option, usually a preamble is available immediately.
     Stale,
   };
+
   /// Schedule an async read of the preamble.
-  /// If there's no preamble yet (because the file was just opened), we'll wait
-  /// for it to build. The result may be null if it fails to build or is empty.
-  /// If an error occurs, it is forwarded to the \p Action callback.
-  /// Context cancellation is ignored and should be handled by the Action.
-  /// (In practice, the Action is almost always executed immediately).
+  /// If there's no preamble yet (because the file was just opened), we'll
+  /// either run \p Action without preamble (when \p AllowFallback is true) or
+  /// wait for it to build. The result may be null if it fails to build or is
+  /// empty. If an error occurs, it is forwarded to the \p Action callback.
+  /// Context cancellation is ignored and should be handled by the Action. (In
+  /// practice, the Action is almost always executed immediately).
   void runWithPreamble(llvm::StringRef Name, PathRef File,
                        PreambleConsistency Consistency,
-                       Callback<InputsAndPreamble> Action);
+                       Callback<InputsAndPreamble> Action,
+                       bool AllowFallback = false);
 
   /// Wait until there are no scheduled or running tasks.
   /// Mostly useful for synchronizing tests.
Index: clangd/TUScheduler.cpp
===================================================================
--- clangd/TUScheduler.cpp
+++ clangd/TUScheduler.cpp
@@ -48,6 +48,8 @@
 #include "index/CanonicalIncludes.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Path.h"
@@ -175,13 +177,16 @@
                                 ParsingCallbacks &Callbacks);
   ~ASTWorker();
 
-  void update(ParseInputs Inputs, WantDiagnostics);
+  void update(FileUpdateInputs Inputs, WantDiagnostics);
   void
   runWithAST(llvm::StringRef Name,
              llvm::unique_function<void(llvm::Expected<InputsAndAST>)> Action);
   bool blockUntilIdle(Deadline Timeout) const;
 
   std::shared_ptr<const PreambleData> getPossiblyStalePreamble() const;
+
+  llvm::Optional<tooling::CompileCommand> getCompileCommand() const;
+
   /// Obtain a preamble reflecting all updates so far. Threadsafe.
   /// It may be delivered immediately, or later on the worker thread.
   void getCurrentPreamble(
@@ -191,6 +196,9 @@
   /// return after an unsuccessful build of the preamble too, i.e. result of
   /// getPossiblyStalePreamble() can be null even after this function returns.
   void waitForFirstPreamble() const;
+  bool isFirstPreambleBuilt() const;
+  void waitForFirstCompileCommand() const;
+  bool isFirstCompileCommandSet() const;
 
   std::size_t getUsedBytes() const;
   bool isASTCached() const;
@@ -247,12 +255,15 @@
   /// Whether the diagnostics for the current FileInputs were reported to the
   /// users before.
   bool DiagsWereReported = false;
-  /// Size of the last AST
   /// Guards members used by both TUScheduler and the worker thread.
   mutable std::mutex Mutex;
   std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
   /// Becomes ready when the first preamble build finishes.
   Notification PreambleWasBuilt;
+  llvm::Optional<tooling::CompileCommand>
+      CompileCommand; /* GUARDED_BY(Mutex) */
+  /// Becomes ready when the first compile command is set.
+  Notification CompileCommandWasSet;
   /// Set to true to signal run() to finish processing.
   bool Done;                    /* GUARDED_BY(Mutex) */
   std::deque<Request> Requests; /* GUARDED_BY(Mutex) */
@@ -349,9 +360,18 @@
 #endif
 }
 
-void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
+void ASTWorker::update(FileUpdateInputs UpdateInputs,
+                       WantDiagnostics WantDiags) {
   llvm::StringRef TaskName = "Update";
   auto Task = [=]() mutable {
+    auto Inputs = std::move(UpdateInputs.InputSansCommand);
+    Inputs.CompileCommand = UpdateInputs.GetCompileCommand();
+    {
+      std::lock_guard<std::mutex> Lock(Mutex);
+      this->CompileCommand = Inputs.CompileCommand;
+    }
+    CompileCommandWasSet.notify();
+
     // Will be used to check if we can avoid rebuilding the AST.
     bool InputsAreTheSame =
         std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
@@ -512,6 +532,11 @@
   return LastBuiltPreamble;
 }
 
+llvm::Optional<tooling::CompileCommand> ASTWorker::getCompileCommand() const {
+  std::lock_guard<std::mutex> Lock(Mutex);
+  return CompileCommand;
+}
+
 void ASTWorker::getCurrentPreamble(
     llvm::unique_function<void(std::shared_ptr<const PreambleData>)> Callback) {
   // We could just call startTask() to throw the read on the queue, knowing
@@ -542,6 +567,18 @@
 
 void ASTWorker::waitForFirstPreamble() const { PreambleWasBuilt.wait(); }
 
+bool ASTWorker::isFirstPreambleBuilt() const {
+  return PreambleWasBuilt.notified();
+}
+
+void ASTWorker::waitForFirstCompileCommand() const {
+  CompileCommandWasSet.wait();
+}
+
+bool ASTWorker::isFirstCompileCommandSet() const {
+  return CompileCommandWasSet.notified();
+}
+
 std::size_t ASTWorker::getUsedBytes() const {
   // Note that we don't report the size of ASTs currently used for processing
   // the in-flight requests. We used this information for debugging purposes
@@ -780,7 +817,6 @@
 struct TUScheduler::FileData {
   /// Latest inputs, passed to TUScheduler::update().
   std::string Contents;
-  tooling::CompileCommand Command;
   ASTWorkerHandle Worker;
 };
 
@@ -823,7 +859,7 @@
   return true;
 }
 
-void TUScheduler::update(PathRef File, ParseInputs Inputs,
+void TUScheduler::update(PathRef File, FileUpdateInputs UpdateInputs,
                          WantDiagnostics WantDiags) {
   std::unique_ptr<FileData> &FD = Files[File];
   if (!FD) {
@@ -832,12 +868,11 @@
         File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr,
         Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, *Callbacks);
     FD = std::unique_ptr<FileData>(new FileData{
-        Inputs.Contents, Inputs.CompileCommand, std::move(Worker)});
+        UpdateInputs.InputSansCommand.Contents, std::move(Worker)});
   } else {
-    FD->Contents = Inputs.Contents;
-    FD->Command = Inputs.CompileCommand;
+    FD->Contents = UpdateInputs.InputSansCommand.Contents;
   }
-  FD->Worker->update(std::move(Inputs), WantDiags);
+  FD->Worker->update(std::move(UpdateInputs), WantDiags);
 }
 
 void TUScheduler::remove(PathRef File) {
@@ -867,9 +902,10 @@
   It->second->Worker->runWithAST(Name, std::move(Action));
 }
 
-void TUScheduler::runWithPreamble(
-    llvm::StringRef Name, PathRef File, PreambleConsistency Consistency,
-    llvm::unique_function<void(llvm::Expected<InputsAndPreamble>)> Action) {
+void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,
+                                  PreambleConsistency Consistency,
+                                  Callback<InputsAndPreamble> Action,
+                                  bool AllowFallback) {
   auto It = Files.find(File);
   if (It == Files.end()) {
     Action(llvm::make_error<LSPError>(
@@ -877,13 +913,44 @@
         ErrorCode::InvalidParams));
     return;
   }
+  // Enter fallback mode if preamble is not ready. We only do this in
+  // asynchronous mode, as TU update should finish before this is run.
+  if (!It->second->Worker->isFirstPreambleBuilt() && AllowFallback &&
+      PreambleTasks) {
+    std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
+    auto FallbackTask = [Worker, this](std::string Name, std::string File,
+                                       std::string Contents, Context Ctx,
+                                       decltype(Action) Action) mutable {
+      std::lock_guard<Semaphore> BarrierLock(Barrier);
+      WithContext Guard(std::move(Ctx));
+      trace::Span Tracer(Name + "(fallback)");
+      SPAN_ATTACH(Tracer, "file", File);
+      Action(InputsAndPreamble{Contents, Worker->getCompileCommand(),
+                               /*Preamble=*/nullptr});
+    };
+
+    PreambleTasks->runAsync(
+        "task(fallback):" + llvm::sys::path::filename(File),
+        Bind(FallbackTask, std::string(Name), std::string(File),
+             It->second->Contents,
+             Context::current().derive(kFileBeingProcessed, File),
+             std::move(Action)));
+    return;
+  }
 
   if (!PreambleTasks) {
+    auto CompileCommand = It->second->Worker->getCompileCommand();
+    if (!CompileCommand) {
+      Action(llvm::make_error<LSPError>(
+          "trying to get preamble for document without compile command",
+          ErrorCode::InvalidParams));
+      return;
+    }
     trace::Span Tracer(Name);
     SPAN_ATTACH(Tracer, "file", File);
     std::shared_ptr<const PreambleData> Preamble =
         It->second->Worker->getPossiblyStalePreamble();
-    Action(InputsAndPreamble{It->second->Contents, It->second->Command,
+    Action(InputsAndPreamble{It->second->Contents, CompileCommand,
                              Preamble.get()});
     return;
   }
@@ -903,10 +970,14 @@
 
   std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
   auto Task = [Worker, this](std::string Name, std::string File,
-                             std::string Contents,
-                             tooling::CompileCommand Command, Context Ctx,
+                             std::string Contents, Context Ctx,
                              decltype(ConsistentPreamble) ConsistentPreamble,
                              decltype(Action) Action) mutable {
+    if (!Worker->isFirstCompileCommandSet()) {
+      Worker->waitForFirstCompileCommand();
+    }
+    auto CompileCommand = Worker->getCompileCommand();
+    assert(CompileCommand.hasValue());
     std::shared_ptr<const PreambleData> Preamble;
     if (ConsistentPreamble.valid()) {
       Preamble = ConsistentPreamble.get();
@@ -922,13 +993,12 @@
     WithContext Guard(std::move(Ctx));
     trace::Span Tracer(Name);
     SPAN_ATTACH(Tracer, "file", File);
-    Action(InputsAndPreamble{Contents, Command, Preamble.get()});
+    Action(InputsAndPreamble{Contents, CompileCommand, Preamble.get()});
   };
 
   PreambleTasks->runAsync(
       "task:" + llvm::sys::path::filename(File),
       Bind(Task, std::string(Name), std::string(File), It->second->Contents,
-           It->second->Command,
            Context::current().derive(kFileBeingProcessed, File),
            std::move(ConsistentPreamble), std::move(Action)));
 }
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -11,7 +11,9 @@
 #include "CodeComplete.h"
 #include "FindSymbols.h"
 #include "Headers.h"
+#include "Protocol.h"
 #include "SourceCode.h"
+#include "TUScheduler.h"
 #include "Trace.h"
 #include "index/CanonicalIncludes.h"
 #include "index/FileIndex.h"
@@ -21,6 +23,7 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
@@ -153,17 +156,21 @@
   if (ClangTidyOptProvider)
     Opts.ClangTidyOpts = ClangTidyOptProvider->getOptions(File);
   Opts.SuggestMissingIncludes = SuggestMissingIncludes;
+
   // FIXME: some build systems like Bazel will take time to preparing
   // environment to build the file, it would be nice if we could emit a
   // "PreparingBuild" status to inform users, it is non-trivial given the
   // current implementation.
-  ParseInputs Inputs;
-  Inputs.CompileCommand = getCompileCommand(File);
-  Inputs.FS = FSProvider.getFileSystem();
-  Inputs.Contents = Contents;
-  Inputs.Opts = std::move(Opts);
-  Inputs.Index = Index;
-  WorkScheduler.update(File, Inputs, WantDiags);
+  FileUpdateInputs UpdateInputs;
+  std::string FilePath = File;
+  UpdateInputs.GetCompileCommand = [this, FilePath]() {
+    return getCompileCommand(FilePath);
+  };
+  UpdateInputs.InputSansCommand.FS = FSProvider.getFileSystem();
+  UpdateInputs.InputSansCommand.Contents = Contents;
+  UpdateInputs.InputSansCommand.Opts = std::move(Opts);
+  UpdateInputs.InputSansCommand.Index = Index;
+  WorkScheduler.update(File, UpdateInputs, WantDiags);
 }
 
 void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); }
@@ -187,7 +194,21 @@
       return CB(IP.takeError());
     if (isCancelled())
       return CB(llvm::make_error<CancelledError>());
+    if (!IP->Preamble) {
+      vlog("File {0} is not ready for code completion. Enter fallback mode.",
+           File);
+      CodeCompleteResult CCR;
+      CCR.Context = CodeCompletionContext::CCC_Recovery;
+
+      // FIXME: perform simple completion e.g. using identifiers in the current
+      // file and symbols in the index.
+      // FIXME: let clients know that we've entered fallback mode.
+
+      return CB(std::move(CCR));
+    }
 
+    assert(IP->Command.hasValue() &&
+           "Compile command must exist if preamble has been built");
     llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind;
     if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) {
       SpecFuzzyFind.emplace();
@@ -200,7 +221,7 @@
     // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
     // both the old and the new version in case only one of them matches.
     CodeCompleteResult Result = clangd::codeComplete(
-        File, IP->Command, IP->Preamble, IP->Contents, Pos, FS, PCHs,
+        File, *IP->Command, IP->Preamble, IP->Contents, Pos, FS, PCHs,
         CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr);
     {
       clang::clangd::trace::Span Tracer("Completion results callback");
@@ -219,7 +240,8 @@
 
   // We use a potentially-stale preamble because latency is critical here.
   WorkScheduler.runWithPreamble("CodeComplete", File, TUScheduler::Stale,
-                                Bind(Task, File.str(), std::move(CB)));
+                                Bind(Task, File.str(), std::move(CB)),
+                                /*AllowFallback=*/true);
 }
 
 void ClangdServer::signatureHelp(PathRef File, Position Pos,
@@ -233,9 +255,9 @@
     if (!IP)
       return CB(IP.takeError());
 
-    auto PreambleData = IP->Preamble;
-    CB(clangd::signatureHelp(File, IP->Command, PreambleData, IP->Contents, Pos,
-                             FS, PCHs, Index));
+    assert(IP->Command.hasValue());
+    CB(clangd::signatureHelp(File, *IP->Command, IP->Preamble, IP->Contents,
+                             Pos, FS, PCHs, Index));
   };
 
   // Unlike code completion, we wait for an up-to-date preamble here.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to