[clang-tools-extra] r321067 - [clangd] Support filtering by fixing scopes in fuzzyFind.

Eric Liu via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 19 03:37:41 PST 2017


Author: ioeric
Date: Tue Dec 19 03:37:40 2017
New Revision: 321067

URL: http://llvm.org/viewvc/llvm-project?rev=321067&view=rev
Log:
[clangd] Support filtering by fixing scopes in fuzzyFind.

Summary: When scopes are specified, only match symbols from scopes.

Reviewers: sammccall

Reviewed By: sammccall

Subscribers: klimek, ilya-biryukov, cfe-commits

Differential Revision: https://reviews.llvm.org/D41367

Modified:
    clang-tools-extra/trunk/clangd/index/Index.h
    clang-tools-extra/trunk/clangd/index/MemIndex.cpp
    clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
    clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp
    clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
    clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp
    clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp

Modified: clang-tools-extra/trunk/clangd/index/Index.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Index.h?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/Index.h (original)
+++ clang-tools-extra/trunk/clangd/index/Index.h Tue Dec 19 03:37:40 2017
@@ -76,8 +76,11 @@ void operator>>(llvm::StringRef HexStr,
 struct Symbol {
   // The ID of the symbol.
   SymbolID ID;
-  // The qualified name of the symbol, e.g. Foo::bar.
-  std::string QualifiedName;
+  // The unqualified name of the symbol, e.g. "bar" (for "n1::n2::bar").
+  std::string Name;
+  // The scope (e.g. namespace) of the symbol, e.g. "n1::n2" (for
+  // "n1::n2::bar").
+  std::string Scope;
   // The symbol information, like symbol kind.
   index::SymbolInfo SymInfo;
   // The location of the canonical declaration of the symbol.
@@ -124,8 +127,16 @@ private:
 
 struct FuzzyFindRequest {
   /// \brief A query string for the fuzzy find. This is matched against symbols'
-  /// qualfified names.
+  /// un-qualified identifiers and should not contain qualifiers like "::".
   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".
+  ///
+  /// A scope must be fully qualified without leading or trailing "::" e.g.
+  /// "n1::n2". "" is interpreted as the global namespace, and "::" is invalid.
+  std::vector<std::string> Scopes;
   /// \brief The maxinum number of candidates to return.
   size_t MaxCandidateCount = UINT_MAX;
 };

Modified: clang-tools-extra/trunk/clangd/index/MemIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/MemIndex.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/MemIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/MemIndex.cpp Tue Dec 19 03:37:40 2017
@@ -8,6 +8,7 @@
 //===-------------------------------------------------------------------===//
 
 #include "MemIndex.h"
+#include "Logger.h"
 
 namespace clang {
 namespace clangd {
@@ -25,20 +26,30 @@ void MemIndex::build(std::shared_ptr<std
   }
 }
 
-bool MemIndex::fuzzyFind(Context & /*Ctx*/, const FuzzyFindRequest &Req,
+bool MemIndex::fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
                          std::function<void(const Symbol &)> Callback) const {
-  std::string LoweredQuery = llvm::StringRef(Req.Query).lower();
+  assert(!StringRef(Req.Query).contains("::") &&
+         "There must be no :: in query.");
+
   unsigned Matched = 0;
   {
     std::lock_guard<std::mutex> Lock(Mutex);
     for (const auto Pair : Index) {
       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)) {
+
+      // Exact match against all possible scopes.
+      bool ScopeMatched = Req.Scopes.empty();
+      for (StringRef Scope : Req.Scopes) {
+        if (Scope == Sym->Scope) {
+          ScopeMatched = true;
+          break;
+        }
+      }
+      if (!ScopeMatched)
+        continue;
+
+      // FIXME(ioeric): use fuzzy matcher.
+      if (StringRef(StringRef(Sym->Name).lower()).contains(Req.Query)) {
         if (++Matched > Req.MaxCandidateCount)
           return false;
         Callback(*Sym);

Modified: clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp Tue Dec 19 03:37:40 2017
@@ -56,6 +56,18 @@ std::string makeAbsolutePath(const Sourc
   }
   return AbsolutePath.str();
 }
+
+// Split a qualified symbol name into scope and unqualified name, e.g. given
+// "a::b::c", return {"a::b", "c"}. Scope is empty if it doesn't exist.
+std::pair<llvm::StringRef, llvm::StringRef>
+splitQualifiedName(llvm::StringRef QName) {
+  assert(!QName.startswith("::") && "Qualified names should not start with ::");
+  size_t Pos = QName.rfind("::");
+  if (Pos == llvm::StringRef::npos)
+    return {StringRef(), QName};
+  return {QName.substr(0, Pos), QName.substr(Pos + 2)};
+}
+
 } // namespace
 
 // Always return true to continue indexing.
@@ -86,7 +98,9 @@ bool SymbolCollector::handleDeclOccurenc
     SymbolLocation Location = {
         makeAbsolutePath(SM, SM.getFilename(D->getLocation())),
         SM.getFileOffset(D->getLocStart()), SM.getFileOffset(D->getLocEnd())};
-    Symbols.insert({std::move(ID), ND->getQualifiedNameAsString(),
+    std::string QName = ND->getQualifiedNameAsString();
+    auto ScopeAndName = splitQualifiedName(QName);
+    Symbols.insert({std::move(ID), ScopeAndName.second, ScopeAndName.first,
                     index::getSymbolInfo(D), std::move(Location)});
   }
 

Modified: clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp Tue Dec 19 03:37:40 2017
@@ -64,7 +64,8 @@ template<> struct MappingTraits<Symbol>
     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(
         IO, Sym.ID);
     IO.mapRequired("ID", NSymbolID->HexString);
-    IO.mapRequired("QualifiedName", Sym.QualifiedName);
+    IO.mapRequired("Name", Sym.Name);
+    IO.mapRequired("Scope", Sym.Scope);
     IO.mapRequired("SymInfo", Sym.SymInfo);
     IO.mapRequired("CanonicalDeclaration", Sym.CanonicalDeclaration);
   }

Modified: clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp Tue Dec 19 03:37:40 2017
@@ -24,7 +24,7 @@ namespace {
 Symbol symbol(llvm::StringRef ID) {
   Symbol Sym;
   Sym.ID = SymbolID(ID);
-  Sym.QualifiedName = ID;
+  Sym.Name = ID;
   return Sym;
 }
 
@@ -37,7 +37,7 @@ std::vector<std::string>
 getSymbolNames(const std::vector<const Symbol *> &Symbols) {
   std::vector<std::string> Names;
   for (const Symbol *Sym : Symbols)
-    Names.push_back(Sym->QualifiedName);
+    Names.push_back(Sym->Name);
   return Names;
 }
 
@@ -92,8 +92,9 @@ std::vector<std::string> match(const Sym
                                const FuzzyFindRequest &Req) {
   std::vector<std::string> Matches;
   auto Ctx = Context::empty();
-  I.fuzzyFind(Ctx, Req,
-              [&](const Symbol &Sym) { Matches.push_back(Sym.QualifiedName); });
+  I.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
+    Matches.push_back(Sym.Scope + (Sym.Scope.empty() ? "" : "::") + Sym.Name);
+  });
   return Matches;
 }
 
@@ -122,7 +123,8 @@ TEST(FileIndexTest, IndexAST) {
       build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
 
   FuzzyFindRequest Req;
-  Req.Query = "ns::";
+  Req.Query = "";
+  Req.Scopes = {"ns"};
   EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X"));
 }
 
@@ -150,9 +152,9 @@ TEST(FileIndexTest, IndexMultiASTAndDedu
       build("f2", "namespace ns { void ff() {} class X {}; }").getPointer());
 
   FuzzyFindRequest Req;
-  Req.Query = "ns::";
-  EXPECT_THAT(match(M, Req),
-              UnorderedElementsAre("ns::f", "ns::X", "ns::ff"));
+  Req.Query = "";
+  Req.Scopes = {"ns"};
+  EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X", "ns::ff"));
 }
 
 TEST(FileIndexTest, RemoveAST) {
@@ -163,7 +165,8 @@ TEST(FileIndexTest, RemoveAST) {
       build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
 
   FuzzyFindRequest Req;
-  Req.Query = "ns::";
+  Req.Query = "";
+  Req.Scopes = {"ns"};
   EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X"));
 
   M.update(Ctx, "f1", nullptr);

Modified: clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp Tue Dec 19 03:37:40 2017
@@ -19,10 +19,17 @@ namespace clangd {
 
 namespace {
 
-Symbol symbol(llvm::StringRef ID) {
+Symbol symbol(llvm::StringRef QName) {
   Symbol Sym;
-  Sym.ID = SymbolID(ID);
-  Sym.QualifiedName = ID;
+  Sym.ID = SymbolID(QName.str());
+  size_t Pos = QName.rfind("::");
+  if (Pos == llvm::StringRef::npos) {
+    Sym.Name = QName;
+    Sym.Scope = "";
+  } else {
+    Sym.Name = QName.substr(Pos + 2);
+    Sym.Scope = QName.substr(0, Pos);
+  }
   return Sym;
 }
 
@@ -31,19 +38,19 @@ struct SlabAndPointers {
   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);
@@ -52,12 +59,24 @@ generateNumSymbols(int Begin, int End,
   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;
   auto Ctx = Context::empty();
-  I.fuzzyFind(Ctx, Req,
-              [&](const Symbol &Sym) { Matches.push_back(Sym.QualifiedName); });
+  I.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
+    Matches.push_back(Sym.Scope + (Sym.Scope.empty() ? "" : "::") + Sym.Name);
+  });
   return Matches;
 }
 
@@ -110,6 +129,56 @@ TEST(MemIndexTest, MemIndexLimitedNumMat
   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

Modified: clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp?rev=321067&r1=321066&r2=321067&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp Tue Dec 19 03:37:40 2017
@@ -31,7 +31,10 @@ using testing::Field;
 using testing::UnorderedElementsAre;
 
 // GMock helpers for matching Symbol.
-MATCHER_P(QName, Name, "") { return arg.second.QualifiedName == Name; }
+MATCHER_P(QName, Name, "") {
+  return (arg.second.Scope + (arg.second.Scope.empty() ? "" : "::") +
+          arg.second.Name) == Name;
+}
 
 namespace clang {
 namespace clangd {
@@ -111,7 +114,8 @@ TEST_F(SymbolCollectorTest, YAMLConversi
   const std::string YAML1 = R"(
 ---
 ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856
-QualifiedName:   'clang::Foo1'
+Name:   'Foo1'
+Scope:   'clang'
 SymInfo:
   Kind:            Function
   Lang:            Cpp
@@ -124,7 +128,8 @@ CanonicalDeclaration:
   const std::string YAML2 = R"(
 ---
 ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF858
-QualifiedName:   'clang::Foo2'
+Name:   'Foo2'
+Scope:   'clang'
 SymInfo:
   Kind:            Function
   Lang:            Cpp




More information about the cfe-commits mailing list