aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2017-12-19 11:37:40 +0000
committerEric Liu <ioeric@google.com>2017-12-19 11:37:40 +0000
commitd572b55b00080681f153134669dcc4d0975725bf (patch)
tree42bcea6280fae55870c304d249d9a923bfce0b15
parentb3f031f8443fa17026a8dbbc6fb880248bea937c (diff)
[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 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@321067 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--clangd/index/Index.h17
-rw-r--r--clangd/index/MemIndex.cpp27
-rw-r--r--clangd/index/SymbolCollector.cpp16
-rw-r--r--clangd/index/SymbolYAML.cpp3
-rw-r--r--unittests/clangd/FileIndexTests.cpp21
-rw-r--r--unittests/clangd/IndexTests.cpp95
-rw-r--r--unittests/clangd/SymbolCollectorTests.cpp11
7 files changed, 152 insertions, 38 deletions
diff --git a/clangd/index/Index.h b/clangd/index/Index.h
index 886e880b..1cb8943a 100644
--- a/clangd/index/Index.h
+++ b/clangd/index/Index.h
@@ -76,8 +76,11 @@ void operator>>(llvm::StringRef HexStr, SymbolID &ID);
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;
};
diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp
index 98896757..cc4c0ed8 100644
--- a/clangd/index/MemIndex.cpp
+++ b/clangd/index/MemIndex.cpp
@@ -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::vector<const Symbol *>> Syms) {
}
}
-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);
diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp
index 5b364291..6b575ab6 100644
--- a/clangd/index/SymbolCollector.cpp
+++ b/clangd/index/SymbolCollector.cpp
@@ -56,6 +56,18 @@ std::string makeAbsolutePath(const SourceManager &SM, StringRef Path) {
}
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::handleDeclOccurence(
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)});
}
diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp
index 9d89cc9a..beb0ab55 100644
--- a/clangd/index/SymbolYAML.cpp
+++ b/clangd/index/SymbolYAML.cpp
@@ -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);
}
diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp
index c79de441..2371d7dc 100644
--- a/unittests/clangd/FileIndexTests.cpp
+++ b/unittests/clangd/FileIndexTests.cpp
@@ -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 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;
}
@@ -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, IndexMultiASTAndDeduplicate) {
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);
diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp
index e94d2127..2852da0b 100644
--- a/unittests/clangd/IndexTests.cpp
+++ b/unittests/clangd/IndexTests.cpp
@@ -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, MemIndexLimitedNumMatches) {
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
diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp
index 7e97a9da..aeaffefb 100644
--- a/unittests/clangd/SymbolCollectorTests.cpp
+++ b/unittests/clangd/SymbolCollectorTests.cpp
@@ -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, YAMLConversions) {
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