llvm-project/clang-tools-extra/unittests/clangd/FileIndexTests.cpp

198 lines
5.3 KiB
C++
Raw Normal View History

//===-- FileIndexTests.cpp ---------------------------*- C++ -*-----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "index/FileIndex.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Frontend/Utils.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using testing::UnorderedElementsAre;
namespace clang {
namespace clangd {
namespace {
Symbol symbol(llvm::StringRef ID) {
Symbol Sym;
Sym.ID = SymbolID(ID);
Sym.Name = ID;
return Sym;
}
void addNumSymbolsToSlab(int Begin, int End, SymbolSlab *Slab) {
for (int i = Begin; i <= End; i++)
Slab->insert(symbol(std::to_string(i)));
}
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->Name);
return Names;
}
TEST(FileSymbolsTest, UpdateAndGet) {
FileSymbols FS;
EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre());
auto Slab = llvm::make_unique<SymbolSlab>();
addNumSymbolsToSlab(1, 3, Slab.get());
FS.update("f1", std::move(Slab));
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3"));
}
TEST(FileSymbolsTest, Overlap) {
FileSymbols FS;
auto Slab = llvm::make_unique<SymbolSlab>();
addNumSymbolsToSlab(1, 3, Slab.get());
FS.update("f1", std::move(Slab));
Slab = llvm::make_unique<SymbolSlab>();
addNumSymbolsToSlab(3, 5, Slab.get());
FS.update("f2", std::move(Slab));
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3", "3", "4", "5"));
}
TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
FileSymbols FS;
auto Slab = llvm::make_unique<SymbolSlab>();
addNumSymbolsToSlab(1, 3, Slab.get());
FS.update("f1", std::move(Slab));
auto Symbols = FS.allSymbols();
EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3"));
FS.update("f1", nullptr);
EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre());
EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3"));
}
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.Scope + (Sym.Scope.empty() ? "" : "::") + Sym.Name).str());
});
return Matches;
}
/// Create an ParsedAST for \p Code. Returns None if \p Code is empty.
llvm::Optional<ParsedAST> build(std::string Path, llvm::StringRef Code) {
Context Ctx = Context::empty();
if (Code.empty())
return llvm::None;
const char *Args[] = {"clang", "-xc++", Path.c_str()};
auto CI = createInvocationFromCommandLine(Args);
auto Buf = llvm::MemoryBuffer::getMemBuffer(Code);
auto AST = ParsedAST::Build(Ctx, std::move(CI), nullptr, std::move(Buf),
std::make_shared<PCHContainerOperations>(),
vfs::getRealFileSystem());
assert(AST.hasValue());
return std::move(*AST);
}
TEST(FileIndexTest, IndexAST) {
FileIndex M;
auto Ctx = Context::empty();
M.update(
Ctx, "f1",
build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
FuzzyFindRequest Req;
Req.Query = "";
Req.Scopes = {"ns"};
EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X"));
}
TEST(FileIndexTest, NoLocal) {
FileIndex M;
auto Ctx = Context::empty();
M.update(
Ctx, "f1",
build("f1", "namespace ns { void f() { int local = 0; } class X {}; }")
.getPointer());
FuzzyFindRequest Req;
Req.Query = "";
EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns", "ns::f", "ns::X"));
}
TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
FileIndex M;
auto Ctx = Context::empty();
M.update(
Ctx, "f1",
build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
M.update(
Ctx, "f2",
build("f2", "namespace ns { void ff() {} class X {}; }").getPointer());
FuzzyFindRequest Req;
Req.Query = "";
Req.Scopes = {"ns"};
EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X", "ns::ff"));
}
TEST(FileIndexTest, RemoveAST) {
FileIndex M;
auto Ctx = Context::empty();
M.update(
Ctx, "f1",
build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
FuzzyFindRequest Req;
Req.Query = "";
Req.Scopes = {"ns"};
EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X"));
M.update(Ctx, "f1", nullptr);
EXPECT_THAT(match(M, Req), UnorderedElementsAre());
}
TEST(FileIndexTest, RemoveNonExisting) {
FileIndex M;
auto Ctx = Context::empty();
M.update(Ctx, "no", nullptr);
EXPECT_THAT(match(M, FuzzyFindRequest()), UnorderedElementsAre());
}
TEST(FileIndexTest, ClassMembers) {
FileIndex M;
auto Ctx = Context::empty();
M.update(Ctx, "f1",
build("f1", "class X { static int m1; int m2;};").getPointer());
FuzzyFindRequest Req;
Req.Query = "";
EXPECT_THAT(match(M, Req), UnorderedElementsAre("X", "X::m1", "X::m2"));
}
} // namespace
} // namespace clangd
} // namespace clang