kadircet updated this revision to Diff 296402.
kadircet marked 6 inline comments as done.
kadircet added a comment.

- Perform sub-scope matching rather than substring match for partial namespaces.
- Apply a penalty for partially matching namespaces.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88814/new/

https://reviews.llvm.org/D88814

Files:
  clang-tools-extra/clangd/FindSymbols.cpp
  clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp

Index: clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
+++ clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
@@ -141,7 +141,10 @@
   EXPECT_THAT(getSymbols(TU, "::"), ElementsAre(QName("ans1")));
   EXPECT_THAT(getSymbols(TU, "::a"), ElementsAre(QName("ans1")));
   EXPECT_THAT(getSymbols(TU, "ans1::"),
-              UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2")));
+              UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2"),
+                                   QName("ans1::ans2::ai2")));
+  EXPECT_THAT(getSymbols(TU, "ans2::"),
+              UnorderedElementsAre(QName("ans1::ans2::ai2")));
   EXPECT_THAT(getSymbols(TU, "::ans1"), ElementsAre(QName("ans1")));
   EXPECT_THAT(getSymbols(TU, "::ans1::"),
               UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2")));
@@ -256,6 +259,17 @@
   EXPECT_THAT(getSymbols(TU, "::"), ElementsAre(QName("func"), QName("ns")));
 }
 
+TEST(WorkspaceSymbols, RankingPartilNamespace) {
+  TestTU TU;
+  TU.Code = R"cpp(
+    namespace ns1 {
+      namespace ns2 { struct Foo {}; }
+    }
+    namespace ns2 { struct FooB {}; })cpp";
+  EXPECT_THAT(getSymbols(TU, "ns2::f"),
+              ElementsAre(QName("ns2::FooB"), QName("ns1::ns2::Foo")));
+}
+
 TEST(WorkspaceSymbols, WithLimit) {
   TestTU TU;
   TU.AdditionalFiles["foo.h"] = R"cpp(
Index: clang-tools-extra/clangd/FindSymbols.cpp
===================================================================
--- clang-tools-extra/clangd/FindSymbols.cpp
+++ clang-tools-extra/clangd/FindSymbols.cpp
@@ -18,6 +18,9 @@
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Index/IndexingAction.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
@@ -38,6 +41,37 @@
   }
 };
 
+// Returns true if \p Query can be found as a sub-scope inside \p Scope.
+bool approximateScopeMatch(llvm::StringRef Scope,
+                           llvm::ArrayRef<llvm::StringRef> Query) {
+  if (Query.empty())
+    return true;
+
+  llvm::SmallVector<llvm::StringRef, 2> Scopes;
+  Scope.split(Scopes, "::");
+
+  llvm::ArrayRef<llvm::StringRef> ScopesRef(Scopes);
+  // We perform a naive string search below, assuming scopes as our characters.
+  while (ScopesRef.size() >= Query.size()) {
+    // Trim Scopes from left until we find the first scope of Query. E.g:
+    // Scopes: a::b::c::
+    // Query:     b::c::
+    // This will trim `a::` from Scopes.
+    ScopesRef = ScopesRef.drop_while(
+        [FirstQueryScope = Query.front()](llvm::StringRef Scope) {
+          return Scope != FirstQueryScope;
+        });
+    if (ScopesRef.size() < Query.size())
+      break;
+    // Check if ScopesRef prefix matches Query.
+    if (ScopesRef.take_front(Query.size()).equals(Query))
+      return true;
+    // If not try with the rest.
+    ScopesRef = ScopesRef.drop_front();
+  }
+  return false;
+}
+
 } // namespace
 
 llvm::Expected<Location> indexToLSPLocation(const SymbolLocation &Loc,
@@ -71,25 +105,48 @@
   if (Query.empty() || !Index)
     return Result;
 
+  // Lookup for qualified names are performed as:
+  // - Exact namespaces are boosted by the index.
+  // - Approximate matches are (sub-scope match) included via AnyScope logic.
+  // - Non-matching namespaces (no sub-scope match) are post-filtered.
   auto Names = splitQualifiedName(Query);
 
   FuzzyFindRequest Req;
   Req.Query = std::string(Names.second);
 
-  // FuzzyFind doesn't want leading :: qualifier
-  bool IsGlobalQuery = Names.first.consume_front("::");
-  // Restrict results to the scope in the query string if present (global or
-  // not).
-  if (IsGlobalQuery || !Names.first.empty())
+  // FuzzyFind doesn't want leading :: qualifier.
+  auto HasLeadingColons = Names.first.consume_front("::");
+  // Limit the query to specific namespace if it is fully-qualified.
+  Req.AnyScope = !HasLeadingColons;
+  // Boost symbols from desired namespace.
+  if (HasLeadingColons || !Names.first.empty())
     Req.Scopes = {std::string(Names.first)};
-  else
-    Req.AnyScope = true;
-  if (Limit)
+  if (Limit) {
     Req.Limit = Limit;
+    // If we are boosting a specific scope allow more results to be retrieved,
+    // since some symbols from preferred namespaces might not make the cut.
+    if (Req.AnyScope && !Req.Scopes.empty())
+      *Req.Limit *= 5;
+  }
   TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
       Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
   FuzzyMatcher Filter(Req.Query);
-  Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
+
+  llvm::SmallVector<llvm::StringRef, 2> ReqScopes;
+  Names.first.consume_back("::");
+  if (!Names.first.empty())
+    Names.first.split(ReqScopes, "::");
+  Index->fuzzyFind(Req, [HintPath, &Top, &Filter, &ReqScopes,
+                         AnyScope = Req.AnyScope,
+                         ReqScope = Names.first](const Symbol &Sym) {
+    std::string Scope = std::string(Sym.Scope);
+    llvm::StringRef ScopeRef = Scope;
+    ScopeRef.consume_back("::");
+    // Fuzzyfind might return symbols from irrelevant namespaces if query was
+    // not fully-qualified, drop those.
+    if (AnyScope && !approximateScopeMatch(ScopeRef, ReqScopes))
+      return;
+
     auto Loc = symbolToLocation(Sym, HintPath);
     if (!Loc) {
       log("Workspace symbols: {0}", Loc.takeError());
@@ -97,9 +154,6 @@
     }
 
     SymbolKind SK = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind);
-    std::string Scope = std::string(Sym.Scope);
-    llvm::StringRef ScopeRef = Scope;
-    ScopeRef.consume_back("::");
     SymbolInformation Info = {(Sym.Name + Sym.TemplateSpecializationArgs).str(),
                               SK, *Loc, std::string(ScopeRef)};
 
@@ -108,6 +162,8 @@
     SymbolRelevanceSignals Relevance;
     Relevance.Name = Sym.Name;
     Relevance.Query = SymbolRelevanceSignals::Generic;
+    // If symbol and request scopes do not match exactly, apply a penalty.
+    Relevance.InBaseClass = AnyScope && ScopeRef != ReqScope;
     if (auto NameMatch = Filter.match(Sym.Name))
       Relevance.NameMatch = *NameMatch;
     else {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to