680 lines · cpp
1//===--------------- PrerequisiteModulesTests.cpp -------------------*- C++2//-*-===//3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9 10/// FIXME: Skip testing on windows temporarily due to the different escaping11/// code mode.12#ifndef _WIN3213 14#include "Annotations.h"15#include "CodeComplete.h"16#include "Compiler.h"17#include "ModulesBuilder.h"18#include "ScanningProjectModules.h"19#include "TestTU.h"20#include "support/ThreadsafeFS.h"21#include "llvm/ADT/StringRef.h"22#include "llvm/Support/FileSystem.h"23#include "llvm/Support/raw_ostream.h"24#include "gmock/gmock.h"25#include "gtest/gtest.h"26 27namespace clang::clangd {28namespace {29 30class GlobalScanningCounterProjectModules : public ProjectModules {31public:32 GlobalScanningCounterProjectModules(33 std::unique_ptr<ProjectModules> Underlying, std::atomic<unsigned> &Count)34 : Underlying(std::move(Underlying)), Count(Count) {}35 36 std::vector<std::string> getRequiredModules(PathRef File) override {37 return Underlying->getRequiredModules(File);38 }39 40 std::string getModuleNameForSource(PathRef File) override {41 return Underlying->getModuleNameForSource(File);42 }43 44 void setCommandMangler(CommandMangler Mangler) override {45 Underlying->setCommandMangler(std::move(Mangler));46 }47 48 std::string getSourceForModuleName(llvm::StringRef ModuleName,49 PathRef RequiredSrcFile) override {50 Count++;51 return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile);52 }53 54private:55 std::unique_ptr<ProjectModules> Underlying;56 std::atomic<unsigned> &Count;57};58 59class MockDirectoryCompilationDatabase : public MockCompilationDatabase {60public:61 MockDirectoryCompilationDatabase(StringRef TestDir, const ThreadsafeFS &TFS)62 : MockCompilationDatabase(TestDir),63 MockedCDBPtr(std::make_shared<MockClangCompilationDatabase>(*this)),64 TFS(TFS), GlobalScanningCount(0) {65 this->ExtraClangFlags.push_back("-std=c++20");66 this->ExtraClangFlags.push_back("-c");67 }68 69 void addFile(llvm::StringRef Path, llvm::StringRef Contents);70 71 std::unique_ptr<ProjectModules> getProjectModules(PathRef) const override {72 return std::make_unique<GlobalScanningCounterProjectModules>(73 scanningProjectModules(MockedCDBPtr, TFS), GlobalScanningCount);74 }75 76 unsigned getGlobalScanningCount() const { return GlobalScanningCount; }77 78private:79 class MockClangCompilationDatabase : public tooling::CompilationDatabase {80 public:81 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)82 : MCDB(MCDB) {}83 84 std::vector<tooling::CompileCommand>85 getCompileCommands(StringRef FilePath) const override {86 std::optional<tooling::CompileCommand> Cmd =87 MCDB.getCompileCommand(FilePath);88 EXPECT_TRUE(Cmd);89 return {*Cmd};90 }91 92 std::vector<std::string> getAllFiles() const override { return Files; }93 94 void AddFile(StringRef File) { Files.push_back(File.str()); }95 96 private:97 MockDirectoryCompilationDatabase &MCDB;98 std::vector<std::string> Files;99 };100 101 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;102 const ThreadsafeFS &TFS;103 104 mutable std::atomic<unsigned> GlobalScanningCount;105};106 107// Add files to the working testing directory and the compilation database.108void MockDirectoryCompilationDatabase::addFile(llvm::StringRef Path,109 llvm::StringRef Contents) {110 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));111 112 SmallString<256> AbsPath(Directory);113 llvm::sys::path::append(AbsPath, Path);114 115 ASSERT_FALSE(116 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));117 118 std::error_code EC;119 llvm::raw_fd_ostream OS(AbsPath, EC);120 ASSERT_FALSE(EC);121 OS << Contents;122 123 MockedCDBPtr->AddFile(Path);124}125 126class PrerequisiteModulesTests : public ::testing::Test {127protected:128 void SetUp() override {129 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("modules-test", TestDir));130 }131 132 void TearDown() override {133 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));134 }135 136public:137 // Get the absolute path for file specified by Path under testing working138 // directory.139 std::string getFullPath(llvm::StringRef Path) {140 SmallString<128> Result(TestDir);141 llvm::sys::path::append(Result, Path);142 EXPECT_TRUE(llvm::sys::fs::exists(Result.str()));143 return Result.str().str();144 }145 146 ParseInputs getInputs(llvm::StringRef FileName,147 const GlobalCompilationDatabase &CDB) {148 std::string FullPathName = getFullPath(FileName);149 150 ParseInputs Inputs;151 std::optional<tooling::CompileCommand> Cmd =152 CDB.getCompileCommand(FullPathName);153 EXPECT_TRUE(Cmd);154 Inputs.CompileCommand = std::move(*Cmd);155 Inputs.TFS = &FS;156 157 if (auto Contents = FS.view(TestDir)->getBufferForFile(FullPathName))158 Inputs.Contents = Contents->get()->getBuffer().str();159 160 return Inputs;161 }162 163 SmallString<256> TestDir;164 // FIXME: It will be better to use the MockFS if the scanning process and165 // build module process doesn't depend on reading real IO.166 RealThreadsafeFS FS;167 168 DiagnosticConsumer DiagConsumer;169};170 171TEST_F(PrerequisiteModulesTests, NonModularTest) {172 MockDirectoryCompilationDatabase CDB(TestDir, FS);173 174 CDB.addFile("foo.h", R"cpp(175inline void foo() {}176 )cpp");177 178 CDB.addFile("NonModular.cpp", R"cpp(179#include "foo.h"180void use() {181 foo();182}183 )cpp");184 185 ModulesBuilder Builder(CDB);186 187 // NonModular.cpp is not related to modules. So nothing should be built.188 auto NonModularInfo =189 Builder.buildPrerequisiteModulesFor(getFullPath("NonModular.cpp"), FS);190 EXPECT_TRUE(NonModularInfo);191 192 HeaderSearchOptions HSOpts;193 NonModularInfo->adjustHeaderSearchOptions(HSOpts);194 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());195 196 auto Invocation =197 buildCompilerInvocation(getInputs("NonModular.cpp", CDB), DiagConsumer);198 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));199}200 201TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {202 MockDirectoryCompilationDatabase CDB(TestDir, FS);203 204 CDB.addFile("foo.h", R"cpp(205inline void foo() {}206 )cpp");207 208 CDB.addFile("M.cppm", R"cpp(209module;210#include "foo.h"211export module M;212 )cpp");213 214 ModulesBuilder Builder(CDB);215 216 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath("M.cppm"), FS);217 EXPECT_TRUE(MInfo);218 219 // Nothing should be built since M doesn't dependent on anything.220 HeaderSearchOptions HSOpts;221 MInfo->adjustHeaderSearchOptions(HSOpts);222 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());223 224 auto Invocation =225 buildCompilerInvocation(getInputs("M.cppm", CDB), DiagConsumer);226 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));227}228 229TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {230 MockDirectoryCompilationDatabase CDB(TestDir, FS);231 232 CDB.ExtraClangFlags.push_back("-invalid-unknown-flag");233 234 CDB.addFile("Dep.cppm", R"cpp(235export module Dep;236 )cpp");237 238 CDB.addFile("M.cppm", R"cpp(239export module M;240import Dep;241 )cpp");242 243 // An invalid flag will break the module compilation and the244 // getRequiredModules would return an empty array245 auto ProjectModules = CDB.getProjectModules(getFullPath("M.cppm"));246 EXPECT_TRUE(247 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());248 249 // Set the mangler to filter out the invalid flag250 ProjectModules->setCommandMangler([](tooling::CompileCommand &Command,251 PathRef) {252 auto const It = llvm::find(Command.CommandLine, "-invalid-unknown-flag");253 Command.CommandLine.erase(It);254 });255 256 // And now it returns a non-empty list of required modules since the257 // compilation succeeded258 EXPECT_FALSE(259 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());260}261 262TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {263 MockDirectoryCompilationDatabase CDB(TestDir, FS);264 265 CDB.addFile("foo.h", R"cpp(266inline void foo() {}267 )cpp");268 269 CDB.addFile("M.cppm", R"cpp(270module;271#include "foo.h"272export module M;273 )cpp");274 275 CDB.addFile("N.cppm", R"cpp(276export module N;277import :Part;278import M;279 )cpp");280 281 CDB.addFile("N-part.cppm", R"cpp(282// Different module name with filename intentionally.283export module N:Part;284 )cpp");285 286 ModulesBuilder Builder(CDB);287 288 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);289 EXPECT_TRUE(NInfo);290 291 ParseInputs NInput = getInputs("N.cppm", CDB);292 std::unique_ptr<CompilerInvocation> Invocation =293 buildCompilerInvocation(NInput, DiagConsumer);294 // Test that `PrerequisiteModules::canReuse` works basically.295 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));296 297 {298 // Check that299 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`300 // can appending HeaderSearchOptions correctly.301 HeaderSearchOptions HSOpts;302 NInfo->adjustHeaderSearchOptions(HSOpts);303 304 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("M"));305 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("N:Part"));306 }307 308 {309 // Check that310 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`311 // can replace HeaderSearchOptions correctly.312 HeaderSearchOptions HSOpts;313 HSOpts.PrebuiltModuleFiles["M"] = "incorrect_path";314 HSOpts.PrebuiltModuleFiles["N:Part"] = "incorrect_path";315 NInfo->adjustHeaderSearchOptions(HSOpts);316 317 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles["M"]).ends_with(".pcm"));318 EXPECT_TRUE(319 StringRef(HSOpts.PrebuiltModuleFiles["N:Part"]).ends_with(".pcm"));320 }321}322 323TEST_F(PrerequisiteModulesTests, ReusabilityTest) {324 MockDirectoryCompilationDatabase CDB(TestDir, FS);325 326 CDB.addFile("foo.h", R"cpp(327inline void foo() {}328 )cpp");329 330 CDB.addFile("M.cppm", R"cpp(331module;332#include "foo.h"333export module M;334 )cpp");335 336 CDB.addFile("N.cppm", R"cpp(337export module N;338import :Part;339import M;340 )cpp");341 342 CDB.addFile("N-part.cppm", R"cpp(343// Different module name with filename intentionally.344export module N:Part;345 )cpp");346 347 ModulesBuilder Builder(CDB);348 349 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);350 EXPECT_TRUE(NInfo);351 EXPECT_TRUE(NInfo);352 353 ParseInputs NInput = getInputs("N.cppm", CDB);354 std::unique_ptr<CompilerInvocation> Invocation =355 buildCompilerInvocation(NInput, DiagConsumer);356 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));357 358 // Test that we can still reuse the NInfo after we touch a unrelated file.359 {360 CDB.addFile("L.cppm", R"cpp(361module;362#include "foo.h"363export module L;364export int ll = 43;365 )cpp");366 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));367 368 CDB.addFile("bar.h", R"cpp(369inline void bar() {}370inline void bar(int) {}371 )cpp");372 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));373 }374 375 // Test that we can't reuse the NInfo after we touch a related file.376 {377 CDB.addFile("M.cppm", R"cpp(378module;379#include "foo.h"380export module M;381export int mm = 44;382 )cpp");383 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));384 385 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);386 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));387 388 CDB.addFile("foo.h", R"cpp(389inline void foo() {}390inline void foo(int) {}391 )cpp");392 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));393 394 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);395 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));396 }397 398 CDB.addFile("N-part.cppm", R"cpp(399export module N:Part;400// Intentioned to make it uncompilable.401export int NPart = 4LIdjwldijaw402 )cpp");403 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));404 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);405 EXPECT_TRUE(NInfo);406 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));407 408 CDB.addFile("N-part.cppm", R"cpp(409export module N:Part;410export int NPart = 43;411 )cpp");412 EXPECT_TRUE(NInfo);413 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));414 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);415 EXPECT_TRUE(NInfo);416 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));417 418 // Test that if we changed the modification time of the file, the module files419 // info is still reusable if its content doesn't change.420 CDB.addFile("N-part.cppm", R"cpp(421export module N:Part;422export int NPart = 43;423 )cpp");424 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));425 426 CDB.addFile("N.cppm", R"cpp(427export module N;428import :Part;429import M;430 431export int nn = 43;432 )cpp");433 // NInfo should be reusable after we change its content.434 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));435}436 437// An End-to-End test for modules.438TEST_F(PrerequisiteModulesTests, ParsedASTTest) {439 MockDirectoryCompilationDatabase CDB(TestDir, FS);440 441 CDB.addFile("A.cppm", R"cpp(442export module A;443export void printA();444 )cpp");445 446 CDB.addFile("Use.cpp", R"cpp(447import A;448)cpp");449 450 ModulesBuilder Builder(CDB);451 452 ParseInputs Use = getInputs("Use.cpp", CDB);453 Use.ModulesManager = &Builder;454 455 std::unique_ptr<CompilerInvocation> CI =456 buildCompilerInvocation(Use, DiagConsumer);457 EXPECT_TRUE(CI);458 459 auto Preamble =460 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,461 /*Callback=*/nullptr);462 EXPECT_TRUE(Preamble);463 EXPECT_TRUE(Preamble->RequiredModules);464 465 auto AST = ParsedAST::build(getFullPath("Use.cpp"), Use, std::move(CI), {},466 Preamble);467 EXPECT_TRUE(AST);468 469 const NamedDecl &D = findDecl(*AST, "printA");470 EXPECT_TRUE(D.isFromASTFile());471}472 473// An end to end test for code complete in modules474TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {475 MockDirectoryCompilationDatabase CDB(TestDir, FS);476 477 CDB.addFile("A.cppm", R"cpp(478export module A;479export void printA();480 )cpp");481 482 llvm::StringLiteral UserContents = R"cpp(483import A;484void func() {485 print^486}487)cpp";488 489 CDB.addFile("Use.cpp", UserContents);490 Annotations Test(UserContents);491 492 ModulesBuilder Builder(CDB);493 494 ParseInputs Use = getInputs("Use.cpp", CDB);495 Use.ModulesManager = &Builder;496 497 std::unique_ptr<CompilerInvocation> CI =498 buildCompilerInvocation(Use, DiagConsumer);499 EXPECT_TRUE(CI);500 501 auto Preamble =502 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,503 /*Callback=*/nullptr);504 EXPECT_TRUE(Preamble);505 EXPECT_TRUE(Preamble->RequiredModules);506 507 auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),508 Preamble.get(), Use, {});509 EXPECT_FALSE(Result.Completions.empty());510 EXPECT_EQ(Result.Completions[0].Name, "printA");511}512 513TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {514 MockDirectoryCompilationDatabase CDB(TestDir, FS);515 516 CDB.addFile("A.cppm", R"cpp(517export module A;518export void printA(int a);519 )cpp");520 521 llvm::StringLiteral UserContents = R"cpp(522import A;523void func() {524 printA(^);525}526)cpp";527 528 CDB.addFile("Use.cpp", UserContents);529 Annotations Test(UserContents);530 531 ModulesBuilder Builder(CDB);532 533 ParseInputs Use = getInputs("Use.cpp", CDB);534 Use.ModulesManager = &Builder;535 536 std::unique_ptr<CompilerInvocation> CI =537 buildCompilerInvocation(Use, DiagConsumer);538 EXPECT_TRUE(CI);539 540 auto Preamble =541 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,542 /*Callback=*/nullptr);543 EXPECT_TRUE(Preamble);544 EXPECT_TRUE(Preamble->RequiredModules);545 546 auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(), *Preamble,547 Use, MarkupKind::PlainText);548 EXPECT_FALSE(Result.signatures.empty());549 EXPECT_EQ(Result.signatures[0].label, "printA(int a) -> void");550 EXPECT_EQ(Result.signatures[0].parameters[0].labelString, "int a");551}552 553TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {554 MockDirectoryCompilationDatabase CDB(TestDir, FS);555 556 CDB.addFile("M.cppm", R"cpp(557export module M;558export int M = 43;559 )cpp");560 CDB.addFile("A.cppm", R"cpp(561export module A;562import M;563export int A = 43 + M;564 )cpp");565 CDB.addFile("B.cppm", R"cpp(566export module B;567import M;568export int B = 44 + M;569 )cpp");570 571 ModulesBuilder Builder(CDB);572 573 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);574 EXPECT_TRUE(AInfo);575 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);576 EXPECT_TRUE(BInfo);577 HeaderSearchOptions HSOptsA(TestDir);578 HeaderSearchOptions HSOptsB(TestDir);579 AInfo->adjustHeaderSearchOptions(HSOptsA);580 BInfo->adjustHeaderSearchOptions(HSOptsB);581 582 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());583 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());584 585 // Check that we're reusing the module files.586 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);587 588 // Update M.cppm to check if the modules builder can update correctly.589 CDB.addFile("M.cppm", R"cpp(590export module M;591export constexpr int M = 43;592 )cpp");593 594 ParseInputs AUse = getInputs("A.cppm", CDB);595 AUse.ModulesManager = &Builder;596 std::unique_ptr<CompilerInvocation> AInvocation =597 buildCompilerInvocation(AUse, DiagConsumer);598 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));599 600 ParseInputs BUse = getInputs("B.cppm", CDB);601 AUse.ModulesManager = &Builder;602 std::unique_ptr<CompilerInvocation> BInvocation =603 buildCompilerInvocation(BUse, DiagConsumer);604 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));605 606 auto NewAInfo =607 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);608 auto NewBInfo =609 Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);610 EXPECT_TRUE(NewAInfo);611 EXPECT_TRUE(NewBInfo);612 HeaderSearchOptions NewHSOptsA(TestDir);613 HeaderSearchOptions NewHSOptsB(TestDir);614 NewAInfo->adjustHeaderSearchOptions(NewHSOptsA);615 NewBInfo->adjustHeaderSearchOptions(NewHSOptsB);616 617 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());618 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());619 620 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);621 // Check that we didn't reuse the old and stale module files.622 EXPECT_NE(NewHSOptsA.PrebuiltModuleFiles, HSOptsA.PrebuiltModuleFiles);623}624 625TEST_F(PrerequisiteModulesTests, ScanningCacheTest) {626 MockDirectoryCompilationDatabase CDB(TestDir, FS);627 628 CDB.addFile("M.cppm", R"cpp(629export module M;630 )cpp");631 CDB.addFile("A.cppm", R"cpp(632export module A;633import M;634 )cpp");635 CDB.addFile("B.cppm", R"cpp(636export module B;637import M;638 )cpp");639 640 ModulesBuilder Builder(CDB);641 642 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);643 Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);644 EXPECT_EQ(CDB.getGlobalScanningCount(), 1u);645}646 647TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileTest) {648 MockDirectoryCompilationDatabase CDB(TestDir, FS);649 650 CDB.addFile("M.cppm", R"cpp(651export module M;652 )cpp");653 654 CDB.addFile("U.cpp", R"cpp(655import M;656 )cpp");657 658 // Use ModulesBuilder to produce the prebuilt module file.659 ModulesBuilder Builder(CDB);660 auto ModuleInfo =661 Builder.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS);662 HeaderSearchOptions HS(TestDir);663 ModuleInfo->adjustHeaderSearchOptions(HS);664 665 CDB.ExtraClangFlags.push_back("-fmodule-file=M=" +666 HS.PrebuiltModuleFiles["M"]);667 ModulesBuilder Builder2(CDB);668 auto ModuleInfo2 =669 Builder2.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS);670 HeaderSearchOptions HS2(TestDir);671 ModuleInfo2->adjustHeaderSearchOptions(HS2);672 673 EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles);674}675 676} // namespace677} // namespace clang::clangd678 679#endif680