Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -21,6 +21,7 @@ Trace.cpp index/FileSymbols.cpp index/Index.cpp + index/FileMemIndexManager.cpp index/MemIndex.cpp index/SymbolCollector.cpp index/SymbolYAML.cpp Index: clangd/index/FileMemIndexManager.h =================================================================== --- /dev/null +++ clangd/index/FileMemIndexManager.h @@ -0,0 +1,39 @@ +//===--- FileMemIndexManager.h - In-memory index for files. -------- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEMEMINDEXMANAGER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEMEMINDEXMANAGER_H + +#include "../ClangdUnit.h" +#include "../Context.h" +#include "FileSymbols.h" +#include "MemIndex.h" + +namespace clang { +namespace clangd { + +/// \brief This manages symbls from a set of files and an in-memory index on +/// all symbols. +class FileMemIndexManager { +public: + /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is + /// nullptr, this removes all symbols from \p Path. + void update(Context &Ctx, PathRef Path, ParsedAST *AST); + + MemIndex &index() { return Index; } + +private: + FileSymbols FSymbols; + MemIndex Index; +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEMEMINDEXMANAGER_H Index: clangd/index/FileMemIndexManager.cpp =================================================================== --- /dev/null +++ clangd/index/FileMemIndexManager.cpp @@ -0,0 +1,48 @@ +//===--- FileMemIndexManager.cpp - In-memory index for files. ------ C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FileMemIndexManager.h" +#include "SymbolCollector.h" +#include "clang/Index/IndexingAction.h" + +namespace clang { +namespace clangd { +namespace { + +/// Retrieves namespace and class level symbols in \p Decls. +std::unique_ptr indexAST(ASTContext &Ctx, + llvm::ArrayRef Decls) { + auto Collector = std::make_shared(); + index::IndexingOptions IndexOpts; + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::All; + IndexOpts.IndexFunctionLocals = false; + + index::indexTopLevelDecls(Ctx, Decls, Collector, IndexOpts); + auto Symbols = llvm::make_unique(); + *Symbols = Collector->takeSymbols(); + return Symbols; +} + +} // namespace + +void FileMemIndexManager::update(Context &Ctx, PathRef Path, + ParsedAST *AST) { + if (!AST) { + FSymbols.update(Path, nullptr); + } else { + auto Slab = indexAST(AST->getASTContext(), AST->getTopLevelDecls()); + FSymbols.update(Path, std::move(Slab)); + } + auto Symbols = FSymbols.allSymbols(); + Index.build(std::move(Symbols)); +} + +} // namespace clangd +} // namespace clang Index: unittests/clangd/IndexTests.cpp =================================================================== --- unittests/clangd/IndexTests.cpp +++ unittests/clangd/IndexTests.cpp @@ -7,8 +7,13 @@ // //===----------------------------------------------------------------------===// +#include "index/FileMemIndexManager.h" #include "index/Index.h" #include "index/MemIndex.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/Optional.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -110,6 +115,86 @@ EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); } +/// Update symbols in \p Path to symbols from the AST of \p Code. If \p Code is +/// empty, all symbols in \p Path are removed. +void updateFile(std::string Path, llvm::StringRef Code, + FileMemIndexManager *M) { + Context Ctx = Context::empty(); + if (Code.empty()) { + M->update(Ctx, Path, nullptr); + return; + } + 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(), + vfs::getRealFileSystem()); + assert(AST.hasValue()); + M->update(Ctx, Path, AST.getPointer()); +} + +TEST(FileMemIndexTest, IndexAST) { + FileMemIndexManager M; + updateFile("f1", "namespace ns { void f() {} class X {}; }", &M); + + FuzzyFindRequest Req; + Req.Query = "ns::"; + EXPECT_THAT(match(M.index(), Req), UnorderedElementsAre("ns::f", "ns::X")); +} + +TEST(FileMemIndexTest, NoLocal) { + FileMemIndexManager M; + updateFile("f1", "namespace ns { void f() { int local = 0; } class X {}; }", + &M); + + FuzzyFindRequest Req; + Req.Query = ""; + EXPECT_THAT(match(M.index(), Req), + UnorderedElementsAre("ns", "ns::f", "ns::X")); +} + +TEST(FileMemIndexTest, IndexMultiASTAndDeduplicate) { + FileMemIndexManager M; + updateFile("f1", "namespace ns { void f() {} class X {}; }", &M); + updateFile("f2", "namespace ns { void ff() {} class X {}; }", &M); + + FuzzyFindRequest Req; + Req.Query = "ns::"; + EXPECT_THAT(match(M.index(), Req), + UnorderedElementsAre("ns::f", "ns::X", "ns::ff")); +} + +TEST(FileMemIndexTest, RemoveAST) { + FileMemIndexManager M; + updateFile("f1", "namespace ns { void f() {} class X {}; }", &M); + + FuzzyFindRequest Req; + Req.Query = "ns::"; + EXPECT_THAT(match(M.index(), Req), UnorderedElementsAre("ns::f", "ns::X")); + + updateFile("f1", "", &M); + EXPECT_THAT(match(M.index(), Req), UnorderedElementsAre()); +} + +TEST(FileMemIndexTest, RemoveNonExisting) { + FileMemIndexManager M; + updateFile("no", "", &M); + EXPECT_THAT(match(M.index(), FuzzyFindRequest()), UnorderedElementsAre()); +} + +TEST(FileMemIndexTest, ClassMembers) { + FileMemIndexManager M; + updateFile("f1", "class X { static int m1; int m2;};", &M); + + FuzzyFindRequest Req; + Req.Query = ""; + EXPECT_THAT(match(M.index(), Req), + UnorderedElementsAre("X", "X::m1", "X::m2")); +} + } // namespace } // namespace clangd } // namespace clang