ioeric created this revision.
ioeric added a reviewer: sammccall.
Herald added subscribers: cfe-commits, ilya-biryukov, klimek.

When scopes are specified, only match symbols from scopes.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41367

Files:
  clangd/index/Index.h
  clangd/index/MemIndex.cpp
  unittests/clangd/IndexTests.cpp

Index: unittests/clangd/IndexTests.cpp
===================================================================
--- unittests/clangd/IndexTests.cpp
+++ unittests/clangd/IndexTests.cpp
@@ -31,27 +31,38 @@
   std::vector<const Symbol *> Pointers;
 };
 
-// Create a slab of symbols with IDs and names [Begin, End]. The life time of
-// the slab is managed by the returned shared pointer. If \p WeakSymbols is
-// provided, it will be pointed to the managed object in the returned shared
-// pointer.
+// Create a slab of symbols with the given qualified names as both IDs and
+// names. The life time of the slab is managed by the returned shared pointer.
+// If \p WeakSymbols is provided, it will be pointed to the managed object in
+// the returned shared pointer.
 std::shared_ptr<std::vector<const Symbol *>>
-generateNumSymbols(int Begin, int End,
-                   std::weak_ptr<SlabAndPointers> *WeakSymbols = nullptr) {
+generateSymbols(std::vector<std::string> QualifiedNames,
+                std::weak_ptr<SlabAndPointers> *WeakSymbols = nullptr) {
   auto Slab = std::make_shared<SlabAndPointers>();
   if (WeakSymbols)
     *WeakSymbols = Slab;
 
-  for (int i = Begin; i <= End; i++)
-    Slab->Slab.insert(symbol(std::to_string(i)));
+  for (llvm::StringRef QName : QualifiedNames)
+    Slab->Slab.insert(symbol(QName));
 
   for (const auto &Sym : Slab->Slab)
     Slab->Pointers.push_back(&Sym.second);
 
   auto *Pointers = &Slab->Pointers;
   return {std::move(Slab), Pointers};
 }
 
+// Create a slab of symbols with IDs and names [Begin, End], otherwise identical
+// to the `generateSymbols` above.
+std::shared_ptr<std::vector<const Symbol *>>
+generateNumSymbols(int Begin, int End,
+                   std::weak_ptr<SlabAndPointers> *WeakSymbols = nullptr) {
+  std::vector<std::string> Names;
+  for (int i = Begin; i <= End; i++)
+    Names.push_back(std::to_string(i));
+  return generateSymbols(Names, WeakSymbols);
+}
+
 std::vector<std::string> match(const SymbolIndex &I,
                                const FuzzyFindRequest &Req) {
   std::vector<std::string> Matches;
@@ -110,6 +121,56 @@
   EXPECT_EQ(Matches.size(), Req.MaxCandidateCount);
 }
 
+TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) {
+  MemIndex I;
+  I.build(generateSymbols({"a::xyz", "b::yz", "yz"}));
+  FuzzyFindRequest Req;
+  Req.Query = "y";
+  auto Matches = match(I, Req);
+  EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz", "b::yz", "yz"));
+}
+
+TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) {
+  MemIndex I;
+  I.build(generateSymbols({"a::xyz", "b::yz", "yz"}));
+  FuzzyFindRequest Req;
+  Req.Query = "y";
+  Req.Scopes.push_back("::");
+  auto Matches = match(I, Req);
+  EXPECT_THAT(match(I, Req), UnorderedElementsAre("yz"));
+}
+
+TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) {
+  MemIndex I;
+  I.build(generateSymbols({"a::xyz", "a::yy", "a::xz", "b::yz", "yz"}));
+  FuzzyFindRequest Req;
+  Req.Query = "y";
+  Req.Scopes.push_back("a");
+  auto Matches = match(I, Req);
+  EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz", "a::yy"));
+}
+
+TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) {
+  MemIndex I;
+  I.build(generateSymbols({"a::xyz", "a::yy", "a::xz", "b::yz", "yz"}));
+  FuzzyFindRequest Req;
+  Req.Query = "y";
+  Req.Scopes.push_back("a");
+  Req.Scopes.push_back("b");
+  auto Matches = match(I, Req);
+  EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz", "a::yy", "b::yz"));
+}
+
+TEST(MemIndexTest, NoMatchNestedScopes) {
+  MemIndex I;
+  I.build(generateSymbols({"a::xyz", "a::b::yy"}));
+  FuzzyFindRequest Req;
+  Req.Query = "y";
+  Req.Scopes.push_back("a");
+  auto Matches = match(I, Req);
+  EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz"));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/index/MemIndex.cpp
===================================================================
--- clangd/index/MemIndex.cpp
+++ clangd/index/MemIndex.cpp
@@ -27,21 +27,41 @@
 
 bool MemIndex::fuzzyFind(Context & /*Ctx*/, const FuzzyFindRequest &Req,
                          std::function<void(const Symbol &)> Callback) const {
-  std::string LoweredQuery = llvm::StringRef(Req.Query).lower();
+  std::vector<std::string> FixedPrefixes;
+  for (StringRef Scope : Req.Scopes)
+    FixedPrefixes.push_back(
+        (Scope == "::" || Scope.empty()) ? "" : (Scope + "::").str());
+
   unsigned Matched = 0;
   {
     std::lock_guard<std::mutex> Lock(Mutex);
     for (const auto Pair : Index) {
+      if (Matched >= Req.MaxCandidateCount)
+        return false;
       const Symbol *Sym = Pair.second;
-      // Find all symbols that contain the query, igoring cases.
-      // FIXME: consider matching chunks in qualified names instead the whole
-      // string.
-      // FIXME: use better matching algorithm, e.g. fuzzy matcher.
-      if (StringRef(StringRef(Sym->QualifiedName).lower())
-              .contains(LoweredQuery)) {
-        if (++Matched > Req.MaxCandidateCount)
-          return false;
-        Callback(*Sym);
+
+      auto Match = [&](llvm::StringRef Name, llvm::StringRef Filter) {
+        // FIXME(ioeric): use fuzzy matcher.
+        if (StringRef(Name.lower()).contains(Req.Query)) {
+          Matched++;
+          Callback(*Sym);
+        }
+      };
+
+      StringRef QName = Sym->QualifiedName;
+      if (FixedPrefixes.empty()) {
+        // No prefix is specified. Simply match the entire qualified name.
+        Match(QName, Req.Query);
+      } else {
+        // Try (exact-)matching against all possible scopes/prefixes.
+        for (StringRef Prefix : FixedPrefixes) {
+          if (!QName.consume_front(Prefix))
+            continue;
+          // Only match symbols that are directly defined in the scope.
+          if (QName.contains("::"))
+            continue;
+          Match(QName, Req.Query);
+        }
       }
     }
   }
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -124,8 +124,14 @@
 
 struct FuzzyFindRequest {
   /// \brief A query string for the fuzzy find. This is matched against symbols'
-  /// qualfified names.
+  /// qualified names. If any scope below is provided, \p Query is only matched
+  /// against symbols in the scope (excl. symbols in nested scopes).
   std::string Query;
+  /// \brief If this is non-empty, symbols must be in at least one of the scopes
+  /// (e.g. namespaces) excluding nested scopes. For example, if a scope "xyz"
+  /// is provided, the matched symbols must be defined in scope "xyz" but not
+  /// "xyz::abc". "" and "::" are interpreted as the global namespace.
+  std::vector<std::string> Scopes;
   /// \brief The maxinum number of candidates to return.
   size_t MaxCandidateCount = UINT_MAX;
 };
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to