kadircet created this revision.
kadircet added reviewers: sammccall, kbobyrev.
Herald added subscribers: usaxena95, jfb, arphaman.
kadircet requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

This enables handling of queries like workspaceSymbols by the last used
external index, which might be null when the query comes from a file specific
context but that file doesn't have any ExternalIndex set (existing behaviour).

This also moves config::Params into Config, rather than just having a file path
inside the Config struct or deducing the environment from Context.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D103179

Files:
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/Config.h
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigProvider.cpp
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/index/ProjectAware.cpp
  clang-tools-extra/clangd/tool/ClangdMain.cpp
  clang-tools-extra/clangd/unittests/ClangdTests.cpp
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
  clang-tools-extra/clangd/unittests/ProjectAwareIndexTests.cpp

Index: clang-tools-extra/clangd/unittests/ProjectAwareIndexTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ProjectAwareIndexTests.cpp
+++ clang-tools-extra/clangd/unittests/ProjectAwareIndexTests.cpp
@@ -82,5 +82,44 @@
   EXPECT_EQ(InvocationCount, 1U);
   return;
 }
+
+TEST(ProjectAware, LastIndex) {
+  IndexFactory Gen = [&](const Config::ExternalIndexSpec &Spec,
+                         AsyncTaskRunner *) { return createIndex(); };
+
+  auto Idx = createProjectAwareIndex(std::move(Gen), true);
+  FuzzyFindRequest Req;
+  Req.Query = "1";
+  Req.AnyScope = true;
+
+  auto QueryWithoutPath = [&] {
+    Config C;
+    WithContextValue With(Config::Key, std::move(C));
+    return match(*Idx, Req);
+  };
+
+  // Empty without any external index before hand.
+  EXPECT_THAT(QueryWithoutPath(), IsEmpty());
+  // Now issue a request that will dispatch to an external index.
+  {
+    Config C;
+    C.Index.External.emplace();
+    C.Index.External->Location = "test";
+    WithContextValue With(Config::Key, std::move(C));
+    EXPECT_THAT(match(*Idx, Req), ElementsAre("1"));
+  }
+  // Should use the same external index.
+  EXPECT_THAT(QueryWithoutPath(), ElementsAre("1"));
+  // Issue a query without an external index.
+  {
+    Config C;
+    C.Parm.Path = "path";
+    WithContextValue With(Config::Key, std::move(C));
+    EXPECT_THAT(match(*Idx, Req), IsEmpty());
+  }
+  // Should use the last index, which was none.
+  EXPECT_THAT(QueryWithoutPath(), IsEmpty());
+  return;
+}
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
@@ -24,6 +24,8 @@
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
 
+using Params = Config::Params;
+
 // Provider that appends an arg to compile flags.
 // The arg is prefix<N>, where N is the times getFragments() was called.
 // It also yields a diagnostic each time it's called.
Index: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
@@ -38,7 +38,7 @@
   CapturedDiags Diags;
   Config Conf;
   Fragment Frag;
-  Params Parm;
+  Config::Params Parm;
 
   bool compileAndApply() {
     Conf = Config();
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -338,7 +338,7 @@
   // Provide conditional config that defines FOO for foo.cc.
   class ConfigProvider : public config::Provider {
     std::vector<config::CompiledFragment>
-    getFragments(const config::Params &,
+    getFragments(const Config::Params &,
                  config::DiagnosticCallback DC) const override {
       config::Fragment F;
       F.If.PathMatch.emplace_back(".*foo.cc");
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===================================================================
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -593,7 +593,7 @@
   config::CompiledFragment Frag;
 
   std::vector<config::CompiledFragment>
-  getFragments(const config::Params &,
+  getFragments(const Config::Params &,
                config::DiagnosticCallback) const override {
     return {Frag};
   }
@@ -647,7 +647,7 @@
       BGPolicy = Config::BackgroundPolicy::Skip;
     }
 
-    Frag = [=](const config::Params &, Config &C) {
+    Frag = [=](const Config::Params &, Config &C) {
       if (CDBSearch)
         C.CompileFlags.CDBSearch = *CDBSearch;
       if (IndexSpec)
Index: clang-tools-extra/clangd/index/ProjectAware.cpp
===================================================================
--- clang-tools-extra/clangd/index/ProjectAware.cpp
+++ clang-tools-extra/clangd/index/ProjectAware.cpp
@@ -20,9 +20,11 @@
 #include "support/Trace.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <atomic>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -72,6 +74,8 @@
                          std::unique_ptr<SymbolIndex>>
       IndexForSpec;
   mutable std::unique_ptr<AsyncTaskRunner> Tasks;
+  // Points at the external index used to resolve last query. Might be null.
+  mutable std::atomic<SymbolIndex *> LastIndex = {};
 
   const IndexFactory Gen;
 };
@@ -128,14 +132,26 @@
 
 SymbolIndex *ProjectAwareIndex::getIndex() const {
   const auto &C = Config::current();
-  if (!C.Index.External)
-    return nullptr;
-  const auto &External = *C.Index.External;
-  std::lock_guard<std::mutex> Lock(Mu);
-  auto Entry = IndexForSpec.try_emplace(External, nullptr);
-  if (Entry.second)
-    Entry.first->getSecond() = Gen(External, Tasks.get());
-  return Entry.first->second.get();
+  auto GetIndexFromSpec =
+      [this](llvm::Optional<Config::ExternalIndexSpec> Spec) -> SymbolIndex * {
+    if (!Spec)
+      return nullptr;
+    const auto &External = *Spec;
+    std::lock_guard<std::mutex> Lock(Mu);
+    auto Entry = IndexForSpec.try_emplace(External, nullptr);
+    if (Entry.second)
+      Entry.first->getSecond() = Gen(External, Tasks.get());
+    return Entry.first->second.get();
+  };
+
+  // Use the last index when request is not originating from a particular file.
+  // This enables features like workspaceSymbols to keep working.
+  // Note that this might set LastIndex to nullptr when the user operates on a
+  // file without an external index. This is deliberate to support users with
+  // files open from projects with and without an external index.
+  if (C.Index.External || !C.Parm.Path.empty())
+    LastIndex = GetIndexFromSpec(C.Index.External);
+  return LastIndex;
 }
 } // namespace
 
Index: clang-tools-extra/clangd/ConfigProvider.h
===================================================================
--- clang-tools-extra/clangd/ConfigProvider.h
+++ clang-tools-extra/clangd/ConfigProvider.h
@@ -17,6 +17,7 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGPROVIDER_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGPROVIDER_H
 
+#include "Config.h"
 #include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/SMLoc.h"
@@ -27,22 +28,9 @@
 
 namespace clang {
 namespace clangd {
-struct Config;
 class ThreadsafeFS;
 namespace config {
 
-/// Describes the context used to evaluate configuration fragments.
-struct Params {
-  /// Absolute path to a source file we're applying the config to. Unix slashes.
-  /// Empty if not configuring a particular file.
-  llvm::StringRef Path;
-  /// Hint that stale data is OK to improve performance (e.g. avoid IO).
-  /// FreshTime sets a bound for how old the data can be.
-  /// By default, providers should validate caches against the data source.
-  std::chrono::steady_clock::time_point FreshTime =
-      std::chrono::steady_clock::time_point::max();
-};
-
 /// Used to report problems in parsing or interpreting a config.
 /// Errors reflect structurally invalid config that should be user-visible.
 /// Warnings reflect e.g. unknown properties that are recoverable.
@@ -55,7 +43,7 @@
 ///
 /// Calling it updates the configuration to reflect settings from the fragment.
 /// Returns true if the condition was met and the settings were used.
-using CompiledFragment = std::function<bool(const Params &, Config &)>;
+using CompiledFragment = std::function<bool(const Config::Params &, Config &)>;
 
 /// A source of configuration fragments.
 /// Generally these providers reflect a fixed policy for obtaining config,
@@ -87,7 +75,7 @@
   static std::unique_ptr<Provider> combine(std::vector<const Provider *>);
 
   /// Build a config based on this provider.
-  Config getConfig(const Params &, DiagnosticCallback) const;
+  Config getConfig(const Config::Params &, DiagnosticCallback) const;
 
 private:
   /// Provide fragments that may be relevant to the file.
@@ -99,7 +87,7 @@
   ///
   /// When parsing/compiling, the DiagnosticCallback is used to report errors.
   virtual std::vector<CompiledFragment>
-  getFragments(const Params &, DiagnosticCallback) const = 0;
+  getFragments(const Config::Params &, DiagnosticCallback) const = 0;
 };
 
 } // namespace config
Index: clang-tools-extra/clangd/ConfigProvider.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigProvider.cpp
+++ clang-tools-extra/clangd/ConfigProvider.cpp
@@ -26,6 +26,8 @@
 namespace clangd {
 namespace config {
 
+using Params = Config::Params;
+
 // Threadsafe cache around reading a YAML config file from disk.
 class FileConfigCache : public FileCache {
   mutable llvm::SmallVector<CompiledFragment, 1> CachedValue;
Index: clang-tools-extra/clangd/ConfigCompile.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigCompile.cpp
+++ clang-tools-extra/clangd/ConfigCompile.cpp
@@ -54,6 +54,8 @@
 namespace config {
 namespace {
 
+using Params = Config::Params;
+
 // Returns an empty stringref if Path is not under FragmentDir. Returns Path
 // as-is when FragmentDir is empty.
 llvm::StringRef configRelative(llvm::StringRef Path,
Index: clang-tools-extra/clangd/Config.h
===================================================================
--- clang-tools-extra/clangd/Config.h
+++ clang-tools-extra/clangd/Config.h
@@ -29,6 +29,7 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringSet.h"
+#include <chrono>
 #include <string>
 #include <vector>
 
@@ -52,6 +53,21 @@
   Config(Config &&) = default;
   Config &operator=(Config &&) = default;
 
+  /// Describes the context used to evaluate configuration fragments.
+  struct Params {
+    /// Absolute path to a source file we're applying the config to. Unix
+    /// slashes. Empty if not configuring a particular file.
+    llvm::StringRef Path;
+    /// Hint that stale data is OK to improve performance (e.g. avoid IO).
+    /// FreshTime sets a bound for how old the data can be.
+    /// By default, providers should validate caches against the data source.
+    std::chrono::steady_clock::time_point FreshTime =
+        std::chrono::steady_clock::time_point::max();
+  };
+
+  /// Information about the environment used when generating the config.
+  Params Parm;
+
   struct CDBSearchSpec {
     enum { Ancestors, FixedDir, NoCDBSearch } Policy = Ancestors;
     // Absolute, native slashes, no trailing slash.
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -274,20 +274,19 @@
         : Provider(Provider), Publish(Publish) {}
 
     Context operator()(llvm::StringRef File) {
-      config::Params Params;
+      Config::Params P;
       // Don't reread config files excessively often.
       // FIXME: when we see a config file change event, use the event timestamp?
-      Params.FreshTime =
-          std::chrono::steady_clock::now() - std::chrono::seconds(5);
+      P.FreshTime = std::chrono::steady_clock::now() - std::chrono::seconds(5);
       llvm::SmallString<256> PosixPath;
       if (!File.empty()) {
         assert(llvm::sys::path::is_absolute(File));
         llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix);
-        Params.Path = PosixPath.str();
+        P.Path = PosixPath.str();
       }
 
       llvm::StringMap<std::vector<Diag>> ReportableDiagnostics;
-      Config C = Provider->getConfig(Params, [&](const llvm::SMDiagnostic &D) {
+      Config C = Provider->getConfig(P, [&](const llvm::SMDiagnostic &D) {
         // Create the map entry even for note diagnostics we don't report.
         // This means that when the file is parsed with no warnings, we
         // publish an empty set of diagnostics, clearing any the client has.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to