24#include "clang/Tooling/Tooling.h"
25#include "llvm/ADT/ScopeExit.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/Support/Chrono.h"
28#include "llvm/Support/CommandLine.h"
29#include "llvm/Support/FileSystem.h"
30#include "llvm/Support/Process.h"
31#include "llvm/Support/VirtualFileSystem.h"
32#include "llvm/Support/raw_ostream.h"
33#include "llvm/TargetParser/Host.h"
34#include "gmock/gmock.h"
35#include "gtest/gtest.h"
40MATCHER_P(named, Name,
"") {
return arg.Name == Name; }
44 GlobalScanningCounterProjectModules(
45 std::unique_ptr<ProjectModules> Underlying, std::atomic<unsigned> &Count)
46 : Underlying(std::move(Underlying)), Count(Count) {}
48 std::vector<std::string> getRequiredModules(
PathRef File)
override {
49 return Underlying->getRequiredModules(
File);
52 std::string getModuleNameForSource(
PathRef File)
override {
53 return Underlying->getModuleNameForSource(
File);
56 void setCommandMangler(CommandMangler Mangler)
override {
57 Underlying->setCommandMangler(std::move(Mangler));
60 std::string getSourceForModuleName(llvm::StringRef ModuleName,
61 PathRef RequiredSrcFile)
override {
63 return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile);
66 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
67 return Underlying->getModuleNameState(ModuleName);
71 std::unique_ptr<ProjectModules> Underlying;
72 std::atomic<unsigned> &Count;
77 PerFileModulesCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
78 : Directory(TestDir), TFS(TFS),
79 ToolingCDB(std::make_shared<IndexedCompilationDatabase>(*this)) {}
81 void addFile(llvm::StringRef
Path, llvm::StringRef Contents,
82 std::vector<std::string> ExtraFlags = {}) {
83 ASSERT_FALSE(llvm::sys::path::is_absolute(
Path));
85 SmallString<256> AbsPath(Directory);
86 llvm::sys::path::append(AbsPath,
Path);
88 ASSERT_FALSE(llvm::sys::fs::create_directories(
89 llvm::sys::path::parent_path(AbsPath)));
92 llvm::raw_fd_ostream OS(AbsPath, EC);
96 std::vector<std::string>
CommandLine = {
"clang",
"-std=c++20",
"-c"};
101 Directory, std::string(AbsPath), std::move(
CommandLine),
"");
102 Files.push_back(std::string(AbsPath));
105 std::optional<tooling::CompileCommand>
108 if (It == Commands.end())
110 tooling::CompileCommand Cmd = It->second;
111 if (llvm::any_of(Cmd.CommandLine, [](llvm::StringRef Arg) {
112 return Arg.starts_with(
"@");
114 auto FS = llvm::vfs::getRealFileSystem();
115 auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()
116 ? llvm::cl::TokenizeWindowsCommandLine
117 : llvm::cl::TokenizeGNUCommandLine;
118 tooling::addExpandedResponseFiles(Cmd.CommandLine, Cmd.Directory,
124 std::optional<ProjectInfo> getProjectInfo(
PathRef)
const override {
125 return ProjectInfo{std::string(Directory)};
133 class IndexedCompilationDatabase :
public tooling::CompilationDatabase {
135 IndexedCompilationDatabase(
const PerFileModulesCompilationDatabase &CDB)
138 std::vector<tooling::CompileCommand>
139 getCompileCommands(StringRef FilePath)
const override {
140 if (
auto Cmd = CDB.getCompileCommand(FilePath))
145 std::vector<std::string> getAllFiles()
const override {
return CDB.Files; }
148 const PerFileModulesCompilationDatabase &CDB;
151 std::string Directory;
152 const ThreadsafeFS &TFS;
153 llvm::StringMap<tooling::CompileCommand> Commands;
154 std::vector<std::string> Files;
155 std::shared_ptr<IndexedCompilationDatabase> ToolingCDB;
158class ModuleUnitRootCompilationDatabase
159 :
public PerFileModulesCompilationDatabase {
161 ModuleUnitRootCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
162 : PerFileModulesCompilationDatabase(TestDir, TFS) {}
164 std::optional<ProjectInfo> getProjectInfo(
PathRef File)
const override {
167 llvm::SmallString<256> Root(
File);
168 llvm::sys::path::remove_filename(Root);
169 return ProjectInfo{std::string(Root)};
175 MockDirectoryCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
176 : MockCompilationDatabase(TestDir),
177 MockedCDBPtr(std::make_shared<MockClangCompilationDatabase>(*this)),
178 TFS(TFS), GlobalScanningCount(0) {
179 this->ExtraClangFlags.push_back(
"-std=c++20");
180 this->ExtraClangFlags.push_back(
"-c");
183 void addFile(llvm::StringRef
Path, llvm::StringRef Contents);
186 return std::make_unique<GlobalScanningCounterProjectModules>(
188 GlobalScanningCount);
191 unsigned getGlobalScanningCount()
const {
return GlobalScanningCount; }
194 class MockClangCompilationDatabase :
public tooling::CompilationDatabase {
196 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
199 std::vector<tooling::CompileCommand>
200 getCompileCommands(StringRef FilePath)
const override {
201 std::optional<tooling::CompileCommand> Cmd =
202 MCDB.getCompileCommand(FilePath);
207 std::vector<std::string> getAllFiles()
const override {
return Files; }
209 void AddFile(StringRef
File) { Files.push_back(
File.str()); }
212 MockDirectoryCompilationDatabase &MCDB;
213 std::vector<std::string> Files;
216 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
217 const ThreadsafeFS &TFS;
219 mutable std::atomic<unsigned> GlobalScanningCount;
223void MockDirectoryCompilationDatabase::addFile(llvm::StringRef
Path,
224 llvm::StringRef Contents) {
225 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
228 llvm::sys::path::append(AbsPath, Path);
231 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
234 llvm::raw_fd_ostream OS(AbsPath, EC);
238 MockedCDBPtr->AddFile(Path);
241class PrerequisiteModulesTests :
public ::testing::Test {
243 void SetUp()
override {
244 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
"modules-test", TestDir));
247 void TearDown()
override {
248 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
254 std::string getFullPath(llvm::StringRef Path) {
255 SmallString<128> Result(TestDir);
256 llvm::sys::path::append(Result, Path);
257 EXPECT_TRUE(llvm::sys::fs::exists(Result.str()));
258 return Result.str().str();
261 ParseInputs getInputs(llvm::StringRef FileName,
262 const GlobalCompilationDatabase &CDB) {
263 std::string FullPathName = getFullPath(FileName);
266 std::optional<tooling::CompileCommand> Cmd =
267 CDB.getCompileCommand(FullPathName);
269 Inputs.CompileCommand = std::move(*Cmd);
272 if (
auto Contents = FS.
view(TestDir)->getBufferForFile(FullPathName))
273 Inputs.Contents = Contents->get()->getBuffer().str();
278 SmallString<256> TestDir;
283 DiagnosticConsumer DiagConsumer;
286TEST_F(PrerequisiteModulesTests, NonModularTest) {
287 MockDirectoryCompilationDatabase CDB(TestDir, FS);
289 CDB.addFile(
"foo.h", R
"cpp(
293 CDB.addFile("NonModular.cpp", R
"cpp(
300 ModulesBuilder Builder(CDB);
303 auto NonModularInfo =
304 Builder.buildPrerequisiteModulesFor(getFullPath(
"NonModular.cpp"), FS);
305 EXPECT_TRUE(NonModularInfo);
307 HeaderSearchOptions HSOpts;
308 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
309 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
313 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));
316TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
317 MockDirectoryCompilationDatabase CDB(TestDir, FS);
319 CDB.addFile(
"foo.h", R
"cpp(
323 CDB.addFile("M.cppm", R
"cpp(
329 ModulesBuilder Builder(CDB);
331 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"M.cppm"), FS);
335 HeaderSearchOptions HSOpts;
336 MInfo->adjustHeaderSearchOptions(HSOpts);
337 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
341 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
344TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
345 MockDirectoryCompilationDatabase CDB(TestDir, FS);
347 CDB.ExtraClangFlags.push_back(
"-invalid-unknown-flag");
349 CDB.addFile(
"Dep.cppm", R
"cpp(
353 CDB.addFile("M.cppm", R
"cpp(
360 auto ProjectModules = CDB.getProjectModules(getFullPath(
"M.cppm"));
362 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
365 ProjectModules->setCommandMangler([](tooling::CompileCommand &Command,
367 auto const It = llvm::find(Command.CommandLine,
"-invalid-unknown-flag");
368 Command.CommandLine.erase(It);
374 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
377TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
378 MockDirectoryCompilationDatabase CDB(TestDir, FS);
380 CDB.addFile(
"foo.h", R
"cpp(
384 CDB.addFile("M.cppm", R
"cpp(
390 CDB.addFile("N.cppm", R
"cpp(
396 CDB.addFile("N-part.cppm", R
"cpp(
397// Different module name with filename intentionally.
401 ModulesBuilder Builder(CDB);
403 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
406 ParseInputs NInput = getInputs(
"N.cppm", CDB);
407 std::unique_ptr<CompilerInvocation> Invocation =
410 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
416 HeaderSearchOptions HSOpts;
417 NInfo->adjustHeaderSearchOptions(HSOpts);
419 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
420 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N:Part"));
427 HeaderSearchOptions HSOpts;
428 HSOpts.PrebuiltModuleFiles[
"M"] =
"incorrect_path";
429 HSOpts.PrebuiltModuleFiles[
"N:Part"] =
"incorrect_path";
430 NInfo->adjustHeaderSearchOptions(HSOpts);
432 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles[
"M"]).ends_with(
".pcm"));
434 StringRef(HSOpts.PrebuiltModuleFiles[
"N:Part"]).ends_with(
".pcm"));
438TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
439 MockDirectoryCompilationDatabase CDB(TestDir, FS);
441 CDB.addFile(
"foo.h", R
"cpp(
445 CDB.addFile("M.cppm", R
"cpp(
451 CDB.addFile("N.cppm", R
"cpp(
457 CDB.addFile("N-part.cppm", R
"cpp(
458// Different module name with filename intentionally.
462 ModulesBuilder Builder(CDB);
464 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
468 ParseInputs NInput = getInputs(
"N.cppm", CDB);
469 std::unique_ptr<CompilerInvocation> Invocation =
471 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
475 CDB.addFile(
"L.cppm", R
"cpp(
481 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
483 CDB.addFile("bar.h", R
"cpp(
485inline void bar(int) {}
487 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
492 CDB.addFile(
"M.cppm", R
"cpp(
498 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
500 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
501 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
503 CDB.addFile(
"foo.h", R
"cpp(
505inline void foo(int) {}
507 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
509 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
510 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
513 CDB.addFile(
"N-part.cppm", R
"cpp(
515// Intentioned to make it uncompilable.
516export int NPart = 4LIdjwldijaw
518 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
519 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
521 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
523 CDB.addFile(
"N-part.cppm", R
"cpp(
525export int NPart = 43;
528 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
529 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
531 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
535 CDB.addFile(
"N-part.cppm", R
"cpp(
537export int NPart = 43;
539 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
541 CDB.addFile("N.cppm", R
"cpp(
549 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
552TEST_F(PrerequisiteModulesTests, CanReuseWithTransitiveNamedModuleImports) {
553 MockDirectoryCompilationDatabase CDB(TestDir, FS);
555 CDB.addFile(
"N.cppm", R
"cpp(
557export inline constexpr int n = 1;
560 CDB.addFile("M.cppm", R
"cpp(
563export inline constexpr int m = n + 1;
566 CDB.addFile("A.cppm", R
"cpp(
569export inline constexpr int a = m + 1;
572 CDB.addFile("Use.cpp", R
"cpp(
574int use() { return a; }
577 ModulesBuilder Builder(CDB);
580 Builder.buildPrerequisiteModulesFor(getFullPath(
"Use.cpp"), FS);
581 ASSERT_TRUE(UseInfo);
583 HeaderSearchOptions HSOpts;
584 UseInfo->adjustHeaderSearchOptions(HSOpts);
585 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"A"));
586 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
587 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N"));
591 ASSERT_TRUE(Invocation);
593 EXPECT_TRUE(UseInfo->canReuse(*Invocation, FS.view(TestDir)));
597TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
598 MockDirectoryCompilationDatabase CDB(TestDir, FS);
600 CDB.addFile(
"A.cppm", R
"cpp(
605 CDB.addFile("Use.cpp", R
"cpp(
609 ModulesBuilder Builder(CDB);
611 ParseInputs Use = getInputs("Use.cpp", CDB);
612 Use.ModulesManager = &Builder;
614 std::unique_ptr<CompilerInvocation> CI =
622 EXPECT_TRUE(
Preamble->RequiredModules);
629 EXPECT_TRUE(
D.isFromASTFile());
633TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
634 MockDirectoryCompilationDatabase CDB(TestDir, FS);
636 CDB.addFile(
"A.cppm", R
"cpp(
641 llvm::StringLiteral UserContents = R"cpp(
648 CDB.addFile("Use.cpp", UserContents);
649 Annotations Test(UserContents);
651 ModulesBuilder Builder(CDB);
653 ParseInputs Use = getInputs(
"Use.cpp", CDB);
654 Use.ModulesManager = &Builder;
656 std::unique_ptr<CompilerInvocation> CI =
664 EXPECT_TRUE(
Preamble->RequiredModules);
666 auto Result =
codeComplete(getFullPath(
"Use.cpp"), Test.point(),
668 EXPECT_FALSE(Result.Completions.empty());
669 EXPECT_EQ(Result.Completions[0].Name,
"printA");
672TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
673 MockDirectoryCompilationDatabase CDB(TestDir, FS);
675 CDB.addFile(
"A.cppm", R
"cpp(
677export void printA(int a);
680 llvm::StringLiteral UserContents = R"cpp(
687 CDB.addFile("Use.cpp", UserContents);
688 Annotations Test(UserContents);
690 ModulesBuilder Builder(CDB);
692 ParseInputs Use = getInputs(
"Use.cpp", CDB);
693 Use.ModulesManager = &Builder;
695 std::unique_ptr<CompilerInvocation> CI =
703 EXPECT_TRUE(
Preamble->RequiredModules);
707 EXPECT_FALSE(Result.signatures.empty());
708 EXPECT_EQ(Result.signatures[0].label,
"printA(int a) -> void");
709 EXPECT_EQ(Result.signatures[0].parameters[0].labelString,
"int a");
712TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
713 MockDirectoryCompilationDatabase CDB(TestDir, FS);
715 CDB.addFile(
"M.cppm", R
"cpp(
719 CDB.addFile("A.cppm", R
"cpp(
722export int A = 43 + M;
724 CDB.addFile("B.cppm", R
"cpp(
727export int B = 44 + M;
730 ModulesBuilder Builder(CDB);
732 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
734 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
736 HeaderSearchOptions HSOptsA(TestDir);
737 HeaderSearchOptions HSOptsB(TestDir);
738 AInfo->adjustHeaderSearchOptions(HSOptsA);
739 BInfo->adjustHeaderSearchOptions(HSOptsB);
741 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
742 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
745 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
748 CDB.addFile(
"M.cppm", R
"cpp(
750export constexpr int M = 43;
753 ParseInputs AUse = getInputs("A.cppm", CDB);
754 AUse.ModulesManager = &Builder;
755 std::unique_ptr<CompilerInvocation> AInvocation =
757 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));
759 ParseInputs BUse = getInputs(
"B.cppm", CDB);
760 AUse.ModulesManager = &Builder;
761 std::unique_ptr<CompilerInvocation> BInvocation =
763 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));
766 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
768 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
769 EXPECT_TRUE(NewAInfo);
770 EXPECT_TRUE(NewBInfo);
771 HeaderSearchOptions NewHSOptsA(TestDir);
772 HeaderSearchOptions NewHSOptsB(TestDir);
773 NewAInfo->adjustHeaderSearchOptions(NewHSOptsA);
774 NewBInfo->adjustHeaderSearchOptions(NewHSOptsB);
776 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
777 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
779 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
782 EXPECT_TRUE(NewAInfo->canReuse(*AInvocation, FS.view(TestDir)));
783 EXPECT_TRUE(NewBInfo->canReuse(*BInvocation, FS.view(TestDir)));
786TEST_F(PrerequisiteModulesTests, ScanningCacheTest) {
787 MockDirectoryCompilationDatabase CDB(TestDir, FS);
789 CDB.addFile(
"M.cppm", R
"cpp(
792 CDB.addFile("A.cppm", R
"cpp(
796 CDB.addFile("B.cppm", R
"cpp(
801 ModulesBuilder Builder(CDB);
803 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
804 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
805 EXPECT_EQ(CDB.getGlobalScanningCount(), 1u);
811TEST_F(PrerequisiteModulesTests, CanReuseWithHeadersInModuleUnit) {
812 MockDirectoryCompilationDatabase CDB(TestDir, FS);
815 CDB.addFile(
"header1.h", R
"cpp(
816inline int getValue() { return 42; }
820 CDB.addFile(
"M.cppm", R
"cpp(
824export int m_value = getValue();
828 CDB.addFile(
"N.cppm", R
"cpp(
835 CDB.addFile(
"N-part.cppm", R
"cpp(
839 ModulesBuilder Builder(CDB);
842 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
845 ParseInputs NInput = getInputs(
"N.cppm", CDB);
846 std::unique_ptr<CompilerInvocation> Invocation =
850 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
854 CDB.addFile(
"header1.h", R
"cpp(
855inline int getValue() { return 43; }
857 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
860 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
862 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
865 CDB.addFile(
"M.cppm", R
"cpp(
869export int m_value = getValue();
870export int m_new_value = 10;
872 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
875 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
877 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
880TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileTest) {
881 MockDirectoryCompilationDatabase CDB(TestDir, FS);
883 CDB.addFile(
"M.cppm", R
"cpp(
887 CDB.addFile("U.cpp", R
"cpp(
892 ModulesBuilder Builder(CDB);
894 Builder.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
895 HeaderSearchOptions HS(TestDir);
896 ModuleInfo->adjustHeaderSearchOptions(HS);
898 CDB.ExtraClangFlags.push_back(
"-fmodule-file=M=" +
899 HS.PrebuiltModuleFiles[
"M"]);
900 ModulesBuilder Builder2(CDB);
902 Builder2.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
903 HeaderSearchOptions HS2(TestDir);
904 ModuleInfo2->adjustHeaderSearchOptions(HS2);
906 EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles);
913TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileWithRelativePath) {
914 MockDirectoryCompilationDatabase CDB(TestDir, FS);
916 CDB.addFile(
"M.cppm", R
"cpp(
918export int m_value = 42;
921 CDB.addFile("U.cpp", R
"cpp(
923int use() { return m_value; }
927 ModulesBuilder Builder(CDB);
929 Builder.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
930 ASSERT_TRUE(ModuleInfo);
932 HeaderSearchOptions HS(TestDir);
933 ModuleInfo->adjustHeaderSearchOptions(HS);
934 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
937 std::string OriginalBMPath = HS.PrebuiltModuleFiles[
"M"];
938 ASSERT_TRUE(llvm::sys::path::is_absolute(OriginalBMPath));
939 ASSERT_TRUE(llvm::sys::fs::exists(OriginalBMPath));
942 SmallString<256> BMSubDir(TestDir);
943 llvm::sys::path::append(BMSubDir,
"prebuilt_modules");
944 ASSERT_FALSE(llvm::sys::fs::create_directories(BMSubDir));
946 SmallString<256> NewBMPath(BMSubDir);
947 llvm::sys::path::append(NewBMPath,
"M.pcm");
950 ASSERT_FALSE(llvm::sys::fs::copy_file(OriginalBMPath, NewBMPath));
951 ASSERT_TRUE(llvm::sys::fs::exists(NewBMPath));
954 std::string RelativeBMPath =
955 llvm::StringRef(NewBMPath).drop_front(TestDir.size() + 1).str();
956 ASSERT_FALSE(RelativeBMPath.empty());
957 ASSERT_TRUE(llvm::sys::path::is_relative(RelativeBMPath));
960 MockDirectoryCompilationDatabase CDBWithRelativePath(TestDir, FS);
962 CDBWithRelativePath.addFile(
"M.cppm", R
"cpp(
964export int m_value = 42;
967 CDBWithRelativePath.addFile("U.cpp", R
"cpp(
969int use() { return m_value; }
973 CDBWithRelativePath.ExtraClangFlags.push_back(
"-fmodule-file=M=" +
977 ModulesBuilder BuilderWithRelativePath(CDBWithRelativePath);
978 auto ModuleInfo2 = BuilderWithRelativePath.buildPrerequisiteModulesFor(
979 getFullPath(
"U.cpp"), FS);
980 ASSERT_TRUE(ModuleInfo2);
982 HeaderSearchOptions HS2(TestDir);
983 ModuleInfo2->adjustHeaderSearchOptions(HS2);
986 ASSERT_EQ(HS2.PrebuiltModuleFiles.count(
"M"), 1u);
987 EXPECT_EQ(HS2.PrebuiltModuleFiles[
"M"], std::string(NewBMPath))
988 <<
"Expected absolute path: " << NewBMPath
989 <<
"\nGot: " << HS2.PrebuiltModuleFiles[
"M"]
990 <<
"\nRelative path used: " << RelativeBMPath;
993TEST_F(PrerequisiteModulesTests,
994 UniqueModuleNameStateResolvedFromCompileCommands) {
995 PerFileModulesCompilationDatabase CDB(TestDir, FS);
997 SmallString<256> MPcm(TestDir);
998 llvm::sys::path::append(MPcm,
"build",
"M.pcm");
1000 CDB.addFile(
"M.cppm", R
"cpp(
1002export int value = 1;
1004 {"--precompile",
"-o", std::string(MPcm)});
1005 CDB.addFile(
"A.cpp", R
"cpp(
1007int useA() { return value; }
1009 {"-fmodule-file=M=" + std::string(MPcm)});
1010 CDB.addFile(
"B.cpp", R
"cpp(
1012int useB() { return value; }
1014 {"-fmodule-file=M=" + std::string(MPcm)});
1016 auto ProjectModules = CDB.getProjectModules(getFullPath(
"A.cpp"));
1017 ASSERT_TRUE(ProjectModules);
1019 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1023TEST_F(PrerequisiteModulesTests,
1024 DuplicateModuleNamesResolvedFromCompileCommands) {
1025 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1027 SmallString<256> APcm(TestDir);
1028 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1029 SmallString<256> BPcm(TestDir);
1030 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1032 CDB.addFile(
"a/M.cppm", R
"cpp(
1034export int onlyA = 1;
1036 {"--precompile",
"-o", std::string(APcm)});
1037 CDB.addFile(
"b/M.cppm", R
"cpp(
1039export int onlyB = 2;
1041 {"--precompile",
"-o", std::string(BPcm)});
1042 CDB.addFile(
"a/Use.cpp", R
"cpp(
1044int useA() { return onlyA; }
1046 {"-fmodule-file=M=" + std::string(APcm)});
1047 CDB.addFile(
"b/Use.cpp", R
"cpp(
1049int useB() { return onlyB; }
1051 {"-fmodule-file=M=" + std::string(BPcm)});
1053 auto ProjectModules = CDB.getProjectModules(getFullPath(
"a/Use.cpp"));
1054 ASSERT_TRUE(ProjectModules);
1055 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1059 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"a/Use.cpp")),
1060 getFullPath(
"a/M.cppm"));
1062 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"b/Use.cpp")),
1063 getFullPath(
"b/M.cppm"));
1066TEST_F(PrerequisiteModulesTests,
1067 DuplicateModuleNamesResolvedFromResponseFiles) {
1068 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1070 SmallString<256> APcm(TestDir);
1071 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1072 SmallString<256> BPcm(TestDir);
1073 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1075 SmallString<256> RspDir(TestDir);
1076 llvm::sys::path::append(RspDir,
"build",
"rsp");
1077 ASSERT_FALSE(llvm::sys::fs::create_directories(RspDir));
1079 SmallString<256> AMRsp(RspDir);
1080 llvm::sys::path::append(AMRsp,
"a-m.rsp");
1083 llvm::raw_fd_ostream OS(AMRsp, EC);
1085 OS <<
"-x c++-module -fmodule-output=" << APcm;
1089 SmallString<256> BMRsp(RspDir);
1090 llvm::sys::path::append(BMRsp,
"b-m.rsp");
1093 llvm::raw_fd_ostream OS(BMRsp, EC);
1095 OS <<
"-x c++-module -fmodule-output=" << BPcm;
1099 SmallString<256> AUseRsp(RspDir);
1100 llvm::sys::path::append(AUseRsp,
"a-use.rsp");
1103 llvm::raw_fd_ostream OS(AUseRsp, EC);
1105 OS <<
"-fmodule-file=M=" << APcm;
1109 SmallString<256> BUseRsp(RspDir);
1110 llvm::sys::path::append(BUseRsp,
"b-use.rsp");
1113 llvm::raw_fd_ostream OS(BUseRsp, EC);
1115 OS <<
"-fmodule-file=M=" << BPcm;
1119 CDB.addFile(
"a/M.cppm", R
"cpp(
1121export int onlyA = 1;
1123 {"@" + std::string(AMRsp)});
1124 CDB.addFile(
"b/M.cppm", R
"cpp(
1126export int onlyB = 2;
1128 {"@" + std::string(BMRsp)});
1129 CDB.addFile(
"a/Use.cpp", R
"cpp(
1131int useA() { return onlyA; }
1133 {"@" + std::string(AUseRsp)});
1134 CDB.addFile(
"b/Use.cpp", R
"cpp(
1136int useB() { return onlyB; }
1138 {"@" + std::string(BUseRsp)});
1140 auto ProjectModules = CDB.getProjectModules(getFullPath(
"a/Use.cpp"));
1141 ASSERT_TRUE(ProjectModules);
1142 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1146 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"a/Use.cpp")),
1147 getFullPath(
"a/M.cppm"));
1149 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"b/Use.cpp")),
1150 getFullPath(
"b/M.cppm"));
1153TEST_F(PrerequisiteModulesTests, DuplicateModuleNamesKeepSeparateBMICache) {
1154 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1156 SmallString<256> APcm(TestDir);
1157 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1158 SmallString<256> BPcm(TestDir);
1159 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1161 CDB.addFile(
"a/M.cppm", R
"cpp(
1163export int onlyA = 1;
1165 {"--precompile",
"-o", std::string(APcm)});
1166 CDB.addFile(
"b/M.cppm", R
"cpp(
1168export int onlyB = 2;
1170 {"--precompile",
"-o", std::string(BPcm)});
1171 CDB.addFile(
"a/Use.cpp", R
"cpp(
1173int useA() { return onlyA; }
1175 {"-fmodule-file=M=" + std::string(APcm)});
1176 CDB.addFile(
"b/Use.cpp", R
"cpp(
1178int useB() { return onlyB; }
1180 {"-fmodule-file=M=" + std::string(BPcm)});
1182 ModulesBuilder Builder(CDB);
1185 Builder.buildPrerequisiteModulesFor(getFullPath(
"a/Use.cpp"), FS);
1187 Builder.buildPrerequisiteModulesFor(getFullPath(
"b/Use.cpp"), FS);
1191 HeaderSearchOptions HSA(TestDir);
1192 HeaderSearchOptions HSB(TestDir);
1193 AInfo->adjustHeaderSearchOptions(HSA);
1194 BInfo->adjustHeaderSearchOptions(HSB);
1195 ASSERT_EQ(HSA.PrebuiltModuleFiles.count(
"M"), 1u);
1196 ASSERT_EQ(HSB.PrebuiltModuleFiles.count(
"M"), 1u);
1197 EXPECT_NE(HSA.PrebuiltModuleFiles[
"M"], HSB.PrebuiltModuleFiles[
"M"]);
1199 auto UseA = getInputs(
"a/Use.cpp", CDB);
1200 UseA.ModulesManager = &Builder;
1203 auto PreambleA =
buildPreamble(getFullPath(
"a/Use.cpp"), *CIA, UseA,
1205 ASSERT_TRUE(PreambleA);
1206 auto ASTA =
ParsedAST::build(getFullPath(
"a/Use.cpp"), UseA, std::move(CIA),
1209 EXPECT_TRUE(
findDecl(*ASTA,
"onlyA").isFromASTFile());
1211 auto UseB = getInputs(
"b/Use.cpp", CDB);
1212 UseB.ModulesManager = &Builder;
1215 auto PreambleB =
buildPreamble(getFullPath(
"b/Use.cpp"), *CIB, UseB,
1217 ASSERT_TRUE(PreambleB);
1218 auto ASTB =
ParsedAST::build(getFullPath(
"b/Use.cpp"), UseB, std::move(CIB),
1221 EXPECT_TRUE(
findDecl(*ASTB,
"onlyB").isFromASTFile());
1224TEST_F(PrerequisiteModulesTests, PersistentModuleCacheReusedAcrossBuilders) {
1225 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1227 CDB.addFile(
"M.cppm", R
"cpp(
1229export int MValue = 43;
1231 CDB.addFile("A.cppm", R
"cpp(
1234export int AValue = MValue;
1237 std::string FirstPCMPath;
1239 ModulesBuilder Builder(CDB);
1240 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1242 HeaderSearchOptions HS(TestDir);
1243 AInfo->adjustHeaderSearchOptions(HS);
1244 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1245 FirstPCMPath = HS.PrebuiltModuleFiles[
"M"];
1246 EXPECT_TRUE(llvm::sys::fs::exists(FirstPCMPath));
1247 EXPECT_TRUE(StringRef(FirstPCMPath).contains(
".cache/clangd/modules"));
1250 EXPECT_FALSE(llvm::sys::fs::exists(FirstPCMPath));
1254 ModulesBuilder Builder(CDB);
1255 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1257 HeaderSearchOptions HS(TestDir);
1258 AInfo->adjustHeaderSearchOptions(HS);
1259 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1260 EXPECT_TRUE(llvm::sys::fs::exists(HS.PrebuiltModuleFiles[
"M"]));
1261 EXPECT_TRUE(StringRef(HS.PrebuiltModuleFiles[
"M"]).contains(
"M-"));
1263 ParseInputs AUse = getInputs(
"A.cppm", CDB);
1264 AUse.ModulesManager = &Builder;
1266 ASSERT_TRUE(Invocation);
1267 EXPECT_TRUE(AInfo->canReuse(*Invocation, FS.view(TestDir)));
1270TEST_F(PrerequisiteModulesTests,
1271 PersistentModuleCacheRebuildsAfterDeletingStalePCM) {
1272 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1274 CDB.addFile(
"M.cppm", R
"cpp(
1276export int MValue = 43;
1278 CDB.addFile("A.cppm", R
"cpp(
1281export int AValue = MValue;
1284 ModulesBuilder Builder(CDB);
1285 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1287 HeaderSearchOptions HS(TestDir);
1288 AInfo->adjustHeaderSearchOptions(HS);
1289 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1290 std::string PCMPath = HS.PrebuiltModuleFiles[
"M"];
1293 llvm::raw_fd_ostream OS(PCMPath, EC);
1300 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1301 ASSERT_TRUE(NewAInfo);
1302 HeaderSearchOptions NewHS(TestDir);
1303 NewAInfo->adjustHeaderSearchOptions(NewHS);
1304 ASSERT_EQ(NewHS.PrebuiltModuleFiles.count(
"M"), 1u);
1305 EXPECT_TRUE(llvm::sys::fs::exists(NewHS.PrebuiltModuleFiles[
"M"]));
1306 EXPECT_TRUE(StringRef(NewHS.PrebuiltModuleFiles[
"M"]).contains(
"M-"));
1308 ParseInputs AUse = getInputs(
"A.cppm", CDB);
1309 AUse.ModulesManager = &Builder;
1311 ASSERT_TRUE(Invocation);
1312 EXPECT_TRUE(NewAInfo->canReuse(*Invocation, FS.view(TestDir)));
1315TEST_F(PrerequisiteModulesTests, PersistentModuleCacheCreatesSourceHashLock) {
1316 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1318 CDB.addFile(
"M.cppm", R
"cpp(
1320export int MValue = 43;
1322 CDB.addFile("A.cppm", R
"cpp(
1325export int AValue = MValue;
1328 ModulesBuilder Builder(CDB);
1329 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1332 HeaderSearchOptions HS(TestDir);
1333 AInfo->adjustHeaderSearchOptions(HS);
1334 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1336 llvm::SmallString<256> PCMPath(HS.PrebuiltModuleFiles[
"M"]);
1337 llvm::sys::path::remove_filename(PCMPath);
1338 llvm::SmallString<256> SourceHashDir(PCMPath);
1339 llvm::sys::path::remove_filename(SourceHashDir);
1340 llvm::SmallString<256> CacheRoot(SourceHashDir);
1341 llvm::sys::path::remove_filename(CacheRoot);
1345 llvm::StringRef SourceDirectoryName =
1346 llvm::sys::path::filename(SourceHashDir);
1348 llvm::StringRef SourceHash = SourceDirectoryName.rsplit(
'-').second;
1349 llvm::SmallString<256> LockPath(CacheRoot);
1350 llvm::sys::path::append(LockPath,
".locks", SourceHash);
1352 EXPECT_TRUE(llvm::sys::fs::exists(LockPath));
1355TEST_F(PrerequisiteModulesTests,
1356 PersistentModuleCacheGCRemovesOldStablePublishedModule) {
1357 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1359 CDB.addFile(
"M.cppm", R
"cpp(
1361export int MValue = 43;
1363 CDB.addFile("A.cppm", R
"cpp(
1366export int AValue = MValue;
1369 llvm::SmallString<256> OrphanPCMPath;
1371 ModulesBuilder Builder(CDB);
1372 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1374 HeaderSearchOptions HS(TestDir);
1375 AInfo->adjustHeaderSearchOptions(HS);
1376 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1378 OrphanPCMPath = HS.PrebuiltModuleFiles[
"M"];
1379 llvm::sys::path::remove_filename(OrphanPCMPath);
1380 llvm::sys::path::append(OrphanPCMPath,
"Orphan.pcm");
1383 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1387 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1390 ASSERT_FALSE(llvm::sys::fs::openFileForWrite(OrphanPCMPath, FD,
1391 llvm::sys::fs::CD_OpenExisting,
1392 llvm::sys::fs::OF_None));
1393 auto CloseFD = llvm::scope_exit(
1394 [&] { llvm::sys::Process::SafelyCloseFileDescriptor(FD); });
1395 llvm::sys::TimePoint<> OldTime =
1396 std::chrono::system_clock::now() - std::chrono::hours(24 * 5);
1397 ASSERT_FALSE(llvm::sys::fs::setLastAccessAndModificationTime(FD, OldTime));
1400 ModulesBuilder Builder(CDB);
1401 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1403 EXPECT_FALSE(llvm::sys::fs::exists(OrphanPCMPath));
1406TEST_F(PrerequisiteModulesTests,
1407 PersistentModuleCacheGCKeepsRecentStablePublishedModule) {
1408 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1410 CDB.addFile(
"M.cppm", R
"cpp(
1412export int MValue = 43;
1414 CDB.addFile("A.cppm", R
"cpp(
1417export int AValue = MValue;
1420 llvm::SmallString<256> OrphanPCMPath;
1422 ModulesBuilder Builder(CDB);
1423 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1425 HeaderSearchOptions HS(TestDir);
1426 AInfo->adjustHeaderSearchOptions(HS);
1427 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1429 OrphanPCMPath = HS.PrebuiltModuleFiles[
"M"];
1430 llvm::sys::path::remove_filename(OrphanPCMPath);
1431 llvm::sys::path::append(OrphanPCMPath,
"Orphan.pcm");
1434 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1438 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1441 ModulesBuilder Builder(CDB);
1442 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1444 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1447TEST_F(PrerequisiteModulesTests,
1448 PersistentModuleCacheGCRemovesOldVersionedModuleFile) {
1449 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1451 CDB.addFile(
"M.cppm", R
"cpp(
1453export int MValue = 43;
1455 CDB.addFile("A.cppm", R
"cpp(
1458export int AValue = MValue;
1461 llvm::SmallString<256> OldVersionedPCMPath;
1463 ModulesBuilder Builder(CDB);
1464 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1466 HeaderSearchOptions HS(TestDir);
1467 AInfo->adjustHeaderSearchOptions(HS);
1468 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1470 OldVersionedPCMPath = HS.PrebuiltModuleFiles[
"M"];
1471 ASSERT_TRUE(llvm::sys::fs::exists(OldVersionedPCMPath));
1474 ASSERT_FALSE(llvm::sys::fs::openFileForWrite(OldVersionedPCMPath, FD,
1475 llvm::sys::fs::CD_OpenExisting,
1476 llvm::sys::fs::OF_None));
1477 auto CloseFD = llvm::scope_exit(
1478 [&] { llvm::sys::Process::SafelyCloseFileDescriptor(FD); });
1479 llvm::sys::TimePoint<> OldTime =
1480 std::chrono::system_clock::now() - std::chrono::hours(24 * 5);
1481 ASSERT_FALSE(llvm::sys::fs::setLastAccessAndModificationTime(FD, OldTime));
1484 ModulesBuilder Builder(CDB);
1485 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1487 EXPECT_FALSE(llvm::sys::fs::exists(OldVersionedPCMPath));
1490TEST_F(PrerequisiteModulesTests,
1491 PersistentModuleCacheGCKeepsRecentVersionedModuleFile) {
1492 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1494 CDB.addFile(
"M.cppm", R
"cpp(
1496export int MValue = 43;
1498 CDB.addFile("A.cppm", R
"cpp(
1501export int AValue = MValue;
1504 auto FirstBuilder = std::make_unique<ModulesBuilder>(CDB);
1506 FirstBuilder->buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1508 HeaderSearchOptions HS(TestDir);
1509 AInfo->adjustHeaderSearchOptions(HS);
1510 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1511 llvm::StringRef CopyOnReadPCMPath = HS.PrebuiltModuleFiles[
"M"];
1512 ASSERT_TRUE(llvm::sys::fs::exists(CopyOnReadPCMPath));
1514 ModulesBuilder SecondBuilder(CDB);
1516 SecondBuilder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1517 ASSERT_TRUE(SecondInfo);
1518 EXPECT_TRUE(llvm::sys::fs::exists(CopyOnReadPCMPath));
1521TEST_F(PrerequisiteModulesTests,
1522 PersistentModuleCacheIgnoresRequiredSourceForOnDiskPath) {
1523 ModuleUnitRootCompilationDatabase CDB(TestDir, FS);
1525 CDB.addFile(
"shared/M.cppm", R
"cpp(
1527export int MValue = 43;
1529 CDB.addFile("projA/A.cppm", R
"cpp(
1532export int AValue = MValue;
1534 CDB.addFile("projB/B.cppm", R
"cpp(
1537export int BValue = MValue;
1540 ModulesBuilder Builder(CDB);
1543 Builder.buildPrerequisiteModulesFor(getFullPath(
"projA/A.cppm"), FS);
1545 Builder.buildPrerequisiteModulesFor(getFullPath(
"projB/B.cppm"), FS);
1549 HeaderSearchOptions HSA(TestDir);
1550 HeaderSearchOptions HSB(TestDir);
1551 AInfo->adjustHeaderSearchOptions(HSA);
1552 BInfo->adjustHeaderSearchOptions(HSB);
1553 ASSERT_EQ(HSA.PrebuiltModuleFiles.count(
"M"), 1u);
1554 ASSERT_EQ(HSB.PrebuiltModuleFiles.count(
"M"), 1u);
1555 EXPECT_TRUE(llvm::sys::fs::exists(HSA.PrebuiltModuleFiles[
"M"]));
1556 EXPECT_TRUE(llvm::sys::fs::exists(HSB.PrebuiltModuleFiles[
"M"]));
1558 llvm::SmallString<256> ExpectedRoot(getFullPath(
"shared/M.cppm"));
1559 llvm::sys::path::remove_filename(ExpectedRoot);
1560 llvm::sys::path::append(ExpectedRoot,
".cache",
"clangd",
"modules");
1562 StringRef(HSA.PrebuiltModuleFiles[
"M"]).starts_with(ExpectedRoot));
1563 EXPECT_TRUE(StringRef(HSA.PrebuiltModuleFiles[
"M"]).contains(
"M.cppm-"));
1565 StringRef(HSB.PrebuiltModuleFiles[
"M"]).starts_with(ExpectedRoot));
1566 EXPECT_TRUE(StringRef(HSB.PrebuiltModuleFiles[
"M"]).contains(
"M.cppm-"));
1569TEST_F(PrerequisiteModulesTests, ModuleImportThroughInclude) {
1570 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1572 Annotations UseCpp(R
"cpp(
1573#include "Header.hpp"
1580 CDB.addFile("M.cppm", R
"cpp(
1582export struct TypeFromModule {};
1585 CDB.addFile("Header.hpp", R
"cpp(
1587struct TypeFromHeader {};
1590 CDB.addFile("Use.cpp", UseCpp.code());
1592 ModulesBuilder Builder(CDB);
1594 auto Inputs = getInputs(
"Use.cpp", CDB);
1595 Inputs.ModulesManager = &Builder;
1596 Inputs.Opts.SkipPreambleBuild =
true;
1605 EXPECT_EQ(
Preamble->Preamble.getBounds().Size, 0u);
1610 EXPECT_TRUE(
AST->getDiagnostics().empty());
1612 auto Result =
codeComplete(getFullPath(
"Use.cpp"), UseCpp.point(),
1614 EXPECT_THAT(Result.Completions,
1615 testing::UnorderedElementsAre(named(
"TypeFromModule"),
1616 named(
"TypeFromHeader")));
1619TEST_F(PrerequisiteModulesTests,
1620 SkipPreambleBuildInvalidatedByNewModuleImport) {
1621 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1623 CDB.addFile(
"Dep.cppm", R
"cpp(
1627 CDB.addFile("Consumer.cpp", R
"cpp(
1632 ModulesBuilder Builder(CDB);
1634 auto Inputs = getInputs(
"Consumer.cpp", CDB);
1635 Inputs.ModulesManager = &Builder;
1636 Inputs.Opts.SkipPreambleBuild =
true;
1645 EXPECT_EQ(
Preamble->Preamble.getBounds().Size, 0u);
1646 ASSERT_TRUE(
Preamble->RequiredModules);
1648 CDB.addFile(
"NewDep.cppm", R
"cpp(
1649export module NewDep;
1653 Inputs.Contents = R
"cpp(
1661 llvm::raw_fd_ostream OS(getFullPath("Consumer.cpp"), EC,
1662 llvm::sys::fs::OF_None);
1664 OS << Inputs.Contents;
1671 getFullPath(
"Consumer.cpp"), *NewCI));
1674TEST_F(PrerequisiteModulesTests, ModuleSemanticHighlighting) {
1675 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1677 llvm::StringRef AnnotatedCode = R
"cpp(
1679 $import[[import]] M;
1680 export module highlight;
1681 $export[[export]] void foo() {
1684 Annotations UseCpp(AnnotatedCode);
1686 CDB.addFile("M.cppm", R
"cpp(
1688export struct TypeFromModule {};
1691 CDB.addFile("Use.cpp", UseCpp.code());
1693 ModulesBuilder Builder(CDB);
1695 auto Inputs = getInputs(
"Use.cpp", CDB);
1696 Inputs.ModulesManager = &Builder;
1697 Inputs.Opts.SkipPreambleBuild =
true;
1715 return llvm::any_of(Actual, [&](
const HighlightingToken &T) {
1716 return T.Kind == Kind &&
T.R == UseCpp.range(Name);
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
Provides compilation arguments used for parsing C and C++ files.
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
An interface to query the modules information in the project.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
std::vector< HighlightingToken > getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens)
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
std::string maybeCaseFoldPath(PathRef Path)
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
bool isPreambleCompatible(const PreambleData &Preamble, const ParseInputs &Inputs, PathRef FileName, const CompilerInvocation &CI)
Returns true if Preamble is reusable for Inputs.
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
std::string Path
A typedef to represent a file path.
CodeCompleteResult codeComplete(PathRef FileName, Position Pos, const PreambleData *Preamble, const ParseInputs &ParseInput, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind)
Gets code completions at a specified Pos in FileName.
SignatureHelp signatureHelp(PathRef FileName, Position Pos, const PreambleData &Preamble, const ParseInputs &ParseInput, MarkupKind DocumentationFormat)
Get signature help at a specified Pos in FileName.
std::unique_ptr< ProjectModules > getProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
Creates the project-modules facade used by clangd.