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/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -88,6 +88,7 @@ TestFS.cpp TestIndex.cpp TestTU.cpp + TestWorkspace.cpp TypeHierarchyTests.cpp TweakTests.cpp TweakTesting.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,33 @@ EXPECT_EQ(Results, 1u); } +TEST(FileIndexTest, RelationsMultiFile) { + TestWorkspace Workspace; + Workspace.addSource("Base.h", "class Base {};"); + Workspace.addMainFile("A.cpp", R"cpp( + #include "Base.h" + class A : public Base {}; + )cpp"); + Workspace.addMainFile("B.cpp", R"cpp( + #include "Base.h" + class B : public Base {}; + )cpp"); + + 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,59 @@ +//===--- 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: + // The difference between addSource() and addMainFile() is that only main + // files will be indexed. + void addSource(llvm::StringRef Filename, llvm::StringRef Code) { + addInput(Filename.str(), {Code.str(), /*IsMainFile=*/false}); + } + void addMainFile(llvm::StringRef Filename, llvm::StringRef Code) { + addInput(Filename.str(), {Code.str(), /*IsMainFile=*/true}); + } + + std::unique_ptr index(); + + Optional openFile(llvm::StringRef Filename); + +private: + struct SourceFile { + std::string Code; + bool IsMainFile = false; + }; + llvm::StringMap Inputs; + TestTU TU; + + void addInput(llvm::StringRef Filename, const SourceFile &Input); +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H diff --git a/clang-tools-extra/clangd/unittests/TestWorkspace.cpp b/clang-tools-extra/clangd/unittests/TestWorkspace.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/TestWorkspace.cpp @@ -0,0 +1,49 @@ +//===--- TestWorkspace.cpp - 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 +// +//===----------------------------------------------------------------------===// + +#include "TestWorkspace.h" + +namespace clang { +namespace clangd { + +std::unique_ptr TestWorkspace::index() { + auto Index = std::make_unique(); + for (const auto &Input : Inputs) { + if (!Input.second.IsMainFile) + continue; + TU.Code = Input.second.Code; + TU.Filename = Input.first().str(); + TU.preamble([&](ASTContext &Ctx, std::shared_ptr PP, + const CanonicalIncludes &CanonIncludes) { + Index->updatePreamble(testPath(Input.first()), "null", Ctx, PP, + CanonIncludes); + }); + ParsedAST MainAST = TU.build(); + Index->updateMain(testPath(Input.first()), MainAST); + } + return Index; +} + +Optional TestWorkspace::openFile(llvm::StringRef Filename) { + auto It = Inputs.find(Filename); + if (It == Inputs.end()) { + ADD_FAILURE() << "Accessing non-existing file: " << Filename; + return llvm::None; + } + TU.Code = It->second.Code; + TU.Filename = It->first().str(); + return TU.build(); +} + +void TestWorkspace::addInput(llvm::StringRef Filename, + const SourceFile &Input) { + Inputs.insert(std::make_pair(Filename, Input)); + TU.AdditionalFiles.insert(std::make_pair(Filename, Input.Code)); +} +} // namespace clangd +} // namespace clang \ No newline at end of file diff --git a/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn b/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn --- a/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn +++ b/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn @@ -96,6 +96,7 @@ "TestFS.cpp", "TestIndex.cpp", "TestTU.cpp", + "TestWorkspace.cpp", "TweakTesting.cpp", "TweakTests.cpp", "TypeHierarchyTests.cpp",