diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp --- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp @@ -230,49 +230,6 @@ FileURI("unittest:///root/B.cc")})); } -TEST_F(BackgroundIndexTest, RelationsMultiFile) { - MockFS FS; - FS.Files[testPath("root/Base.h")] = "class Base {};"; - FS.Files[testPath("root/A.cc")] = R"cpp( - #include "Base.h" - class A : public Base {}; - )cpp"; - FS.Files[testPath("root/B.cc")] = R"cpp( - #include "Base.h" - class B : public Base {}; - )cpp"; - - llvm::StringMap Storage; - size_t CacheHits = 0; - MemoryShardStorage MSS(Storage, CacheHits); - OverlayCDB CDB(/*Base=*/nullptr); - BackgroundIndex Index(FS, CDB, [&](llvm::StringRef) { return &MSS; }, - /*Opts=*/{}); - - tooling::CompileCommand Cmd; - Cmd.Filename = testPath("root/A.cc"); - Cmd.Directory = testPath("root"); - Cmd.CommandLine = {"clang++", Cmd.Filename}; - CDB.setCompileCommand(testPath("root/A.cc"), Cmd); - ASSERT_TRUE(Index.blockUntilIdleForTest()); - - Cmd.Filename = testPath("root/B.cc"); - Cmd.CommandLine = {"clang++", Cmd.Filename}; - CDB.setCompileCommand(testPath("root/B.cc"), Cmd); - ASSERT_TRUE(Index.blockUntilIdleForTest()); - - auto HeaderShard = MSS.loadShard(testPath("root/Base.h")); - EXPECT_NE(HeaderShard, nullptr); - SymbolID Base = findSymbol(*HeaderShard->Symbols, "Base").ID; - - RelationsRequest Req; - Req.Subjects.insert(Base); - Req.Predicate = RelationKind::BaseOf; - uint32_t Results = 0; - Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; }); - EXPECT_EQ(Results, 2u); -} - TEST_F(BackgroundIndexTest, MainFileRefs) { MockFS FS; FS.Files[testPath("root/A.h")] = R"cpp( diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp --- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp @@ -14,6 +14,7 @@ #include "SyncAPI.h" #include "TestFS.h" #include "TestTU.h" +#include "TestWorkspace.h" #include "URI.h" #include "index/CanonicalIncludes.h" #include "index/FileIndex.h" @@ -426,6 +427,34 @@ EXPECT_EQ(Results, 1u); } +TEST(FileIndexTest, RelationsMultiFile) { + TestWorkspace Workspace({{"Base.h", "class Base {};", false}, + {"A.cpp", R"cpp( + #include "Base.h" + class A : public Base {}; + )cpp", + true}, + {"B.cpp", R"cpp( + #include "Base.h" + class B : public Base {}; + )cpp", + true}}); + + auto Index = Workspace.index(); + FuzzyFindRequest FFReq; + FFReq.Query = "Base"; + FFReq.AnyScope = true; + SymbolID Base; + Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; }); + + RelationsRequest Req; + Req.Subjects.insert(Base); + Req.Predicate = RelationKind::BaseOf; + uint32_t Results = 0; + Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; }); + EXPECT_EQ(Results, 2u); +} + TEST(FileIndexTest, ReferencesInMainFileWithPreamble) { TestTU TU; TU.HeaderCode = "class Foo{};"; diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -79,7 +79,8 @@ // By default, build() will report Error diagnostics as GTest errors. // Suppress this behavior by adding an 'error-ok' comment to the code. ParsedAST build() const; - std::shared_ptr preamble() const; + std::shared_ptr + preamble(PreambleParsedCallback PreambleCallback = nullptr) const; ParseInputs inputs(MockFS &FS) const; SymbolSlab headerSymbols() const; RefSlab headerRefs() const; diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -80,7 +80,8 @@ } } -std::shared_ptr TestTU::preamble() const { +std::shared_ptr +TestTU::preamble(PreambleParsedCallback PreambleCallback) const { MockFS FS; auto Inputs = inputs(FS); IgnoreDiagnostics Diags; @@ -91,8 +92,7 @@ auto ModuleCacheDeleter = llvm::make_scope_exit( std::bind(deleteModuleCache, CI->getHeaderSearchOpts().ModuleCachePath)); return clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, - /*StoreInMemory=*/true, - /*PreambleCallback=*/nullptr); + /*StoreInMemory=*/true, PreambleCallback); } ParsedAST TestTU::build() const { diff --git a/clang-tools-extra/clangd/unittests/TestWorkspace.h b/clang-tools-extra/clangd/unittests/TestWorkspace.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/TestWorkspace.h @@ -0,0 +1,101 @@ +//===--- TestWorkspace.h - Utility for writing multi-file tests --*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// TestWorkspace builds on TestTU to provide a way to write tests involving +// several related files with inclusion relationships between them. +// +// The tests can exercise both index- and AST-based operations. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H + +#include "TestFS.h" +#include "TestTU.h" +#include "index/FileIndex.h" +#include "index/Index.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +namespace clang { +namespace clangd { + +class TestWorkspace { +public: + struct SourceFile { + std::string Filename; + std::string Code; + bool IsMainFile = false; + }; + + TestWorkspace() {} + TestWorkspace(const std::vector &Inputs) { + for (auto &Input : Inputs) { + addInput(Input); + } + } + + void addSource(llvm::StringRef Filename, llvm::StringRef Code) { + addInput({std::string(Filename), std::string(Code), false}); + } + + void addMainFile(llvm::StringRef Filename, llvm::StringRef Code) { + addInput({std::string(Filename), std::string(Code), true}); + } + + std::unique_ptr index() { + auto Index = std::make_unique(); + for (const auto &Input : Inputs) { + if (Input.IsMainFile) { + TU.Code = Input.Code; + TU.Filename = Input.Filename; + TU.preamble([&](ASTContext &Ctx, + std::shared_ptr PP, + const CanonicalIncludes &CanonIncludes) { + Index->updatePreamble(testPath(Input.Filename), "null", Ctx, PP, + CanonIncludes); + }); + ParsedAST MainAST = TU.build(); + Index->updateMain(testPath(Input.Filename), MainAST); + } + } + return Index; + } + + Optional openFile(llvm::StringRef Filename) { + SourceFile *Input = findFile(Filename); + if (!Input) + return llvm::None; + TU.Code = Input->Code; + TU.Filename = std::string(Filename); + return TU.build(); + } + +private: + std::vector Inputs; + TestTU TU; + + void addInput(const SourceFile &Input) { + Inputs.push_back(Input); + TU.AdditionalFiles.insert(std::make_pair(Input.Filename, Input.Code)); + } + + SourceFile *findFile(llvm::StringRef Filename) { + for (auto &Input : Inputs) + if (Filename == Input.Filename) + return &Input; + return nullptr; + } +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H