blob: 8ef4313d194d00479c9e049e93461c254c2deeaf [file] [log] [blame]
//===-- 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 "TestFS.h"
#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;
}
std::unique_ptr<SymbolSlab> numSlab(int Begin, int End) {
SymbolSlab::Builder Slab;
for (int i = Begin; i <= End; i++)
Slab.insert(symbol(std::to_string(i)));
return llvm::make_unique<SymbolSlab>(std::move(Slab).build());
}
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());
FS.update("f1", numSlab(1, 3));
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3"));
}
TEST(FileSymbolsTest, Overlap) {
FileSymbols FS;
FS.update("f1", numSlab(1, 3));
FS.update("f2", numSlab(3, 5));
EXPECT_THAT(getSymbolNames(*FS.allSymbols()),
UnorderedElementsAre("1", "2", "3", "3", "4", "5"));
}
TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
FileSymbols FS;
FS.update("f1", numSlab(1, 3));
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;
I.fuzzyFind(Req, [&](const Symbol &Sym) {
Matches.push_back((Sym.Scope + Sym.Name).str());
});
return Matches;
}
/// Create an ParsedAST for \p Code. Returns None if \p Code is empty.
/// \p Code is put into <Path>.h which is included by \p <BasePath>.cpp.
llvm::Optional<ParsedAST> build(llvm::StringRef BasePath,
llvm::StringRef Code) {
if (Code.empty())
return llvm::None;
assert(llvm::sys::path::extension(BasePath).empty() &&
"BasePath must be a base file path without extension.");
llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS(
new vfs::InMemoryFileSystem);
std::string Path = (BasePath + ".cpp").str();
std::string Header = (BasePath + ".h").str();
VFS->addFile(Path, 0, llvm::MemoryBuffer::getMemBuffer(""));
VFS->addFile(Header, 0, llvm::MemoryBuffer::getMemBuffer(Code));
const char *Args[] = {"clang", "-xc++", "-include", Header.c_str(),
Path.c_str()};
auto CI = createInvocationFromCommandLine(Args);
auto Buf = llvm::MemoryBuffer::getMemBuffer(Code);
auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf),
std::make_shared<PCHContainerOperations>(), VFS);
assert(AST.hasValue());
return std::move(*AST);
}
TEST(FileIndexTest, IndexAST) {
FileIndex M;
M.update(
"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;
M.update(
"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;
M.update(
"f1",
build("f1", "namespace ns { void f() {} class X {}; }").getPointer());
M.update(
"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;
M.update(
"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("f1", nullptr);
EXPECT_THAT(match(M, Req), UnorderedElementsAre());
}
TEST(FileIndexTest, RemoveNonExisting) {
FileIndex M;
M.update("no", nullptr);
EXPECT_THAT(match(M, FuzzyFindRequest()), UnorderedElementsAre());
}
TEST(FileIndexTest, IgnoreClassMembers) {
FileIndex M;
M.update("f1",
build("f1", "class X { static int m1; int m2; static void f(); };")
.getPointer());
FuzzyFindRequest Req;
Req.Query = "";
EXPECT_THAT(match(M, Req), UnorderedElementsAre("X"));
}
TEST(FileIndexTest, NoIncludeCollected) {
FileIndex M;
M.update("f", build("f", "class string {};").getPointer());
FuzzyFindRequest Req;
Req.Query = "";
bool SeenSymbol = false;
M.fuzzyFind(Req, [&](const Symbol &Sym) {
EXPECT_TRUE(Sym.Detail->IncludeHeader.empty());
SeenSymbol = true;
});
EXPECT_TRUE(SeenSymbol);
}
} // namespace
} // namespace clangd
} // namespace clang