25#include "clang/Tooling/Tooling.h"
26#include "llvm/ADT/ScopeExit.h"
27#include "llvm/ADT/StringRef.h"
28#include "llvm/Support/Chrono.h"
29#include "llvm/Support/CommandLine.h"
30#include "llvm/Support/FileSystem.h"
31#include "llvm/Support/Process.h"
32#include "llvm/Support/VirtualFileSystem.h"
33#include "llvm/Support/raw_ostream.h"
34#include "llvm/TargetParser/Host.h"
35#include "gmock/gmock.h"
36#include "gtest/gtest.h"
41MATCHER_P(named, Name,
"") {
return arg.Name == Name; }
45 GlobalScanningCounterProjectModules(
46 std::unique_ptr<ProjectModules> Underlying, std::atomic<unsigned> &Count)
47 : Underlying(std::move(Underlying)), Count(Count) {}
49 std::vector<std::string> getRequiredModules(
PathRef File)
override {
50 return Underlying->getRequiredModules(
File);
53 std::string getModuleNameForSource(
PathRef File)
override {
54 return Underlying->getModuleNameForSource(
File);
57 void setCommandMangler(CommandMangler Mangler)
override {
58 Underlying->setCommandMangler(std::move(Mangler));
61 std::string getSourceForModuleName(llvm::StringRef ModuleName,
62 PathRef RequiredSrcFile)
override {
64 return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile);
67 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
68 return Underlying->getModuleNameState(ModuleName);
72 std::unique_ptr<ProjectModules> Underlying;
73 std::atomic<unsigned> &Count;
78 PerFileModulesCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
79 : Directory(TestDir), TFS(TFS),
80 ToolingCDB(std::make_shared<IndexedCompilationDatabase>(*this)) {}
82 void addFile(llvm::StringRef
Path, llvm::StringRef Contents,
83 std::vector<std::string> ExtraFlags = {}) {
84 ASSERT_FALSE(llvm::sys::path::is_absolute(
Path));
86 SmallString<256> AbsPath(Directory);
87 llvm::sys::path::append(AbsPath,
Path);
89 ASSERT_FALSE(llvm::sys::fs::create_directories(
90 llvm::sys::path::parent_path(AbsPath)));
93 llvm::raw_fd_ostream OS(AbsPath, EC);
97 std::vector<std::string>
CommandLine = {
"clang",
"-std=c++20",
"-c"};
102 Directory, std::string(AbsPath), std::move(
CommandLine),
"");
103 Files.push_back(std::string(AbsPath));
106 std::optional<tooling::CompileCommand>
109 if (It == Commands.end())
111 tooling::CompileCommand Cmd = It->second;
112 if (llvm::any_of(Cmd.CommandLine, [](llvm::StringRef Arg) {
113 return Arg.starts_with(
"@");
115 auto FS = llvm::vfs::getRealFileSystem();
116 auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()
117 ? llvm::cl::TokenizeWindowsCommandLine
118 : llvm::cl::TokenizeGNUCommandLine;
119 tooling::addExpandedResponseFiles(Cmd.CommandLine, Cmd.Directory,
125 std::optional<ProjectInfo> getProjectInfo(
PathRef)
const override {
126 return ProjectInfo{std::string(Directory)};
134 class IndexedCompilationDatabase :
public tooling::CompilationDatabase {
136 IndexedCompilationDatabase(
const PerFileModulesCompilationDatabase &CDB)
139 std::vector<tooling::CompileCommand>
140 getCompileCommands(StringRef FilePath)
const override {
141 if (
auto Cmd = CDB.getCompileCommand(FilePath))
146 std::vector<std::string> getAllFiles()
const override {
return CDB.Files; }
149 const PerFileModulesCompilationDatabase &CDB;
152 std::string Directory;
153 const ThreadsafeFS &TFS;
154 llvm::StringMap<tooling::CompileCommand> Commands;
155 std::vector<std::string> Files;
156 std::shared_ptr<IndexedCompilationDatabase> ToolingCDB;
159class ModuleUnitRootCompilationDatabase
160 :
public PerFileModulesCompilationDatabase {
162 ModuleUnitRootCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
163 : PerFileModulesCompilationDatabase(TestDir, TFS) {}
165 std::optional<ProjectInfo> getProjectInfo(
PathRef File)
const override {
168 llvm::SmallString<256> Root(
File);
169 llvm::sys::path::remove_filename(Root);
170 return ProjectInfo{std::string(Root)};
176 MockDirectoryCompilationDatabase(StringRef TestDir,
const ThreadsafeFS &TFS)
177 : MockCompilationDatabase(TestDir),
178 MockedCDBPtr(std::make_shared<MockClangCompilationDatabase>(*this)),
179 TFS(TFS), GlobalScanningCount(0) {
180 this->ExtraClangFlags.push_back(
"-std=c++20");
181 this->ExtraClangFlags.push_back(
"-c");
184 void addFile(llvm::StringRef
Path, llvm::StringRef Contents);
187 return std::make_unique<GlobalScanningCounterProjectModules>(
189 GlobalScanningCount);
192 unsigned getGlobalScanningCount()
const {
return GlobalScanningCount; }
195 class MockClangCompilationDatabase :
public tooling::CompilationDatabase {
197 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
200 std::vector<tooling::CompileCommand>
201 getCompileCommands(StringRef FilePath)
const override {
202 std::optional<tooling::CompileCommand> Cmd =
203 MCDB.getCompileCommand(FilePath);
208 std::vector<std::string> getAllFiles()
const override {
return Files; }
210 void AddFile(StringRef
File) { Files.push_back(
File.str()); }
213 MockDirectoryCompilationDatabase &MCDB;
214 std::vector<std::string> Files;
217 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
218 const ThreadsafeFS &TFS;
220 mutable std::atomic<unsigned> GlobalScanningCount;
224void MockDirectoryCompilationDatabase::addFile(llvm::StringRef
Path,
225 llvm::StringRef Contents) {
226 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
229 llvm::sys::path::append(AbsPath, Path);
232 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
235 llvm::raw_fd_ostream OS(AbsPath, EC);
239 MockedCDBPtr->AddFile(Path);
242class PrerequisiteModulesTests :
public ::testing::Test {
244 void SetUp()
override {
245 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
"modules-test", TestDir));
248 ASSERT_FALSE(llvm::sys::fs::real_path(TestDir, TestDir));
251 void TearDown()
override {
252 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
258 std::string getFullPath(llvm::StringRef Path) {
259 SmallString<128> Result(TestDir);
260 llvm::sys::path::append(Result, Path);
261 EXPECT_TRUE(llvm::sys::fs::exists(Result.str()));
262 return Result.str().str();
265 ParseInputs getInputs(llvm::StringRef FileName,
266 const GlobalCompilationDatabase &CDB) {
267 std::string FullPathName = getFullPath(FileName);
270 std::optional<tooling::CompileCommand> Cmd =
271 CDB.getCompileCommand(FullPathName);
273 Inputs.CompileCommand = std::move(*Cmd);
276 if (
auto Contents = FS.
view(TestDir)->getBufferForFile(FullPathName))
277 Inputs.Contents = Contents->get()->getBuffer().str();
282 SmallString<256> TestDir;
287 DiagnosticConsumer DiagConsumer;
290TEST_F(PrerequisiteModulesTests, NonModularTest) {
291 MockDirectoryCompilationDatabase CDB(TestDir, FS);
293 CDB.addFile(
"foo.h", R
"cpp(
297 CDB.addFile("NonModular.cpp", R
"cpp(
304 ModulesBuilder Builder(CDB);
307 auto NonModularInfo =
308 Builder.buildPrerequisiteModulesFor(getFullPath(
"NonModular.cpp"), FS);
309 EXPECT_TRUE(NonModularInfo);
311 HeaderSearchOptions HSOpts;
312 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
313 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
317 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));
320TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
321 MockDirectoryCompilationDatabase CDB(TestDir, FS);
323 CDB.addFile(
"foo.h", R
"cpp(
327 CDB.addFile("M.cppm", R
"cpp(
333 ModulesBuilder Builder(CDB);
335 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"M.cppm"), FS);
339 HeaderSearchOptions HSOpts;
340 MInfo->adjustHeaderSearchOptions(HSOpts);
341 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
345 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
348TEST_F(PrerequisiteModulesTests, ObservedModuleOutsideCompilationDatabase) {
349 MockDirectoryCompilationDatabase CDB(TestDir, FS);
350 CDB.addFile(
"Use.cpp", R
"cpp(
354 SmallString<256> ModulePath(TestDir);
355 llvm::sys::path::append(ModulePath, "M.cppm");
357 llvm::raw_fd_ostream OS(ModulePath, EC);
359 OS <<
"export module M;";
362 ModulesBuilder Builder(CDB);
363 EXPECT_TRUE(Builder.observeSourcePath(ModulePath));
364 EXPECT_FALSE(Builder.observeSourcePath(ModulePath));
365 auto Info = Builder.buildPrerequisiteModulesFor(getFullPath(
"Use.cpp"), FS);
367 HeaderSearchOptions HSOpts;
368 Info->adjustHeaderSearchOptions(HSOpts);
369 EXPECT_EQ(HSOpts.PrebuiltModuleFiles.count(
"M"), 1u);
372TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
373 MockDirectoryCompilationDatabase CDB(TestDir, FS);
375 CDB.ExtraClangFlags.push_back(
"-invalid-unknown-flag");
377 CDB.addFile(
"Dep.cppm", R
"cpp(
381 CDB.addFile("M.cppm", R
"cpp(
388 auto ProjectModules = CDB.getProjectModules(getFullPath(
"M.cppm"));
390 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
393 ProjectModules->setCommandMangler([](tooling::CompileCommand &Command,
395 auto const It = llvm::find(Command.CommandLine,
"-invalid-unknown-flag");
396 Command.CommandLine.erase(It);
402 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
405TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
406 MockDirectoryCompilationDatabase CDB(TestDir, FS);
408 CDB.addFile(
"foo.h", R
"cpp(
412 CDB.addFile("M.cppm", R
"cpp(
418 CDB.addFile("N.cppm", R
"cpp(
424 CDB.addFile("N-part.cppm", R
"cpp(
425// Different module name with filename intentionally.
429 ModulesBuilder Builder(CDB);
431 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
434 ParseInputs NInput = getInputs(
"N.cppm", CDB);
435 std::unique_ptr<CompilerInvocation> Invocation =
438 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
444 HeaderSearchOptions HSOpts;
445 NInfo->adjustHeaderSearchOptions(HSOpts);
447 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
448 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N:Part"));
455 HeaderSearchOptions HSOpts;
456 HSOpts.PrebuiltModuleFiles[
"M"] =
"incorrect_path";
457 HSOpts.PrebuiltModuleFiles[
"N:Part"] =
"incorrect_path";
458 NInfo->adjustHeaderSearchOptions(HSOpts);
460 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles[
"M"]).ends_with(
".pcm"));
462 StringRef(HSOpts.PrebuiltModuleFiles[
"N:Part"]).ends_with(
".pcm"));
466TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
467 MockDirectoryCompilationDatabase CDB(TestDir, FS);
469 CDB.addFile(
"foo.h", R
"cpp(
473 CDB.addFile("M.cppm", R
"cpp(
479 CDB.addFile("N.cppm", R
"cpp(
485 CDB.addFile("N-part.cppm", R
"cpp(
486// Different module name with filename intentionally.
490 ModulesBuilder Builder(CDB);
492 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
496 ParseInputs NInput = getInputs(
"N.cppm", CDB);
497 std::unique_ptr<CompilerInvocation> Invocation =
499 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
503 CDB.addFile(
"L.cppm", R
"cpp(
509 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
511 CDB.addFile("bar.h", R
"cpp(
513inline void bar(int) {}
515 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
520 CDB.addFile(
"M.cppm", R
"cpp(
526 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
528 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
529 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
531 CDB.addFile(
"foo.h", R
"cpp(
533inline void foo(int) {}
535 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
537 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
538 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
541 CDB.addFile(
"N-part.cppm", R
"cpp(
543// Intentioned to make it uncompilable.
544export int NPart = 4LIdjwldijaw
546 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
547 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
549 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
551 CDB.addFile(
"N-part.cppm", R
"cpp(
553export int NPart = 43;
556 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
557 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
559 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
563 CDB.addFile(
"N-part.cppm", R
"cpp(
565export int NPart = 43;
567 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
569 CDB.addFile("N.cppm", R
"cpp(
577 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
580TEST_F(PrerequisiteModulesTests, CanReuseWithTransitiveNamedModuleImports) {
581 MockDirectoryCompilationDatabase CDB(TestDir, FS);
583 CDB.addFile(
"N.cppm", R
"cpp(
585export inline constexpr int n = 1;
588 CDB.addFile("M.cppm", R
"cpp(
591export inline constexpr int m = n + 1;
594 CDB.addFile("A.cppm", R
"cpp(
597export inline constexpr int a = m + 1;
600 CDB.addFile("Use.cpp", R
"cpp(
602int use() { return a; }
605 ModulesBuilder Builder(CDB);
608 Builder.buildPrerequisiteModulesFor(getFullPath(
"Use.cpp"), FS);
609 ASSERT_TRUE(UseInfo);
611 HeaderSearchOptions HSOpts;
612 UseInfo->adjustHeaderSearchOptions(HSOpts);
613 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"A"));
614 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
615 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N"));
619 ASSERT_TRUE(Invocation);
621 EXPECT_TRUE(UseInfo->canReuse(*Invocation, FS.view(TestDir)));
625TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
626 MockDirectoryCompilationDatabase CDB(TestDir, FS);
628 CDB.addFile(
"A.cppm", R
"cpp(
633 CDB.addFile("Use.cpp", R
"cpp(
637 ModulesBuilder Builder(CDB);
639 ParseInputs Use = getInputs("Use.cpp", CDB);
640 Use.ModulesManager = &Builder;
642 std::unique_ptr<CompilerInvocation> CI =
650 EXPECT_TRUE(
Preamble->RequiredModules);
657 EXPECT_TRUE(
D.isFromASTFile());
660TEST_F(PrerequisiteModulesTests, LocateImportedModule) {
661 MockDirectoryCompilationDatabase CDB(TestDir, FS);
663 Annotations Dep(R
"cpp(
664export $decl[[module]] dep.one.two;
666 CDB.addFile("Dep.cppm", Dep.code());
668 Annotations Part(R
"cpp(
669export $decl[[module]] M:part.one;
671 CDB.addFile("M-part.cppm", Part.code());
673 Annotations Use(R
"cpp(
675import $dep0^dep.$dep1^one.$dep2^two;
676import :$part0^part.$part1^one;
678 CDB.addFile("M.cppm", Use.code());
680 ModulesBuilder Builder(CDB);
681 auto Inputs = getInputs(
"M.cppm", CDB);
682 Inputs.ModulesManager = &Builder;
683 Inputs.Opts.SkipPreambleBuild =
true;
695 ASSERT_TRUE(
AST->getDiagnostics().empty());
697 auto Check = [&](llvm::StringRef Point, llvm::StringRef Name,
698 llvm::StringRef
File, Range TargetRange) {
700 ASSERT_THAT(Results, testing::SizeIs(1));
701 EXPECT_EQ(Results.front().Name, Name);
705 EXPECT_EQ(Results.front().PreferredDeclaration, Target);
706 EXPECT_EQ(Results.front().Definition, Target);
709 for (llvm::StringRef Point : {
"dep0",
"dep1",
"dep2"})
710 Check(Point,
"dep.one.two",
"Dep.cppm", Dep.range(
"decl"));
711 for (llvm::StringRef Point : {
"part0",
"part1"})
712 Check(Point,
"M:part.one",
"M-part.cppm", Part.range(
"decl"));
716TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
717 MockDirectoryCompilationDatabase CDB(TestDir, FS);
719 CDB.addFile(
"A.cppm", R
"cpp(
724 llvm::StringLiteral UserContents = R"cpp(
731 CDB.addFile("Use.cpp", UserContents);
732 Annotations Test(UserContents);
734 ModulesBuilder Builder(CDB);
736 ParseInputs Use = getInputs(
"Use.cpp", CDB);
737 Use.ModulesManager = &Builder;
739 std::unique_ptr<CompilerInvocation> CI =
747 EXPECT_TRUE(
Preamble->RequiredModules);
749 auto Result =
codeComplete(getFullPath(
"Use.cpp"), Test.point(),
751 EXPECT_FALSE(Result.Completions.empty());
752 EXPECT_EQ(Result.Completions[0].Name,
"printA");
755TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
756 MockDirectoryCompilationDatabase CDB(TestDir, FS);
758 CDB.addFile(
"A.cppm", R
"cpp(
760export void printA(int a);
763 llvm::StringLiteral UserContents = R"cpp(
770 CDB.addFile("Use.cpp", UserContents);
771 Annotations Test(UserContents);
773 ModulesBuilder Builder(CDB);
775 ParseInputs Use = getInputs(
"Use.cpp", CDB);
776 Use.ModulesManager = &Builder;
778 std::unique_ptr<CompilerInvocation> CI =
786 EXPECT_TRUE(
Preamble->RequiredModules);
790 EXPECT_FALSE(Result.signatures.empty());
791 EXPECT_EQ(Result.signatures[0].label,
"printA(int a) -> void");
792 EXPECT_EQ(Result.signatures[0].parameters[0].labelString,
"int a");
795TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
796 MockDirectoryCompilationDatabase CDB(TestDir, FS);
798 CDB.addFile(
"M.cppm", R
"cpp(
802 CDB.addFile("A.cppm", R
"cpp(
805export int A = 43 + M;
807 CDB.addFile("B.cppm", R
"cpp(
810export int B = 44 + M;
813 ModulesBuilder Builder(CDB);
815 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
817 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
819 HeaderSearchOptions HSOptsA(TestDir);
820 HeaderSearchOptions HSOptsB(TestDir);
821 AInfo->adjustHeaderSearchOptions(HSOptsA);
822 BInfo->adjustHeaderSearchOptions(HSOptsB);
824 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
825 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
828 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
831 CDB.addFile(
"M.cppm", R
"cpp(
833export constexpr int M = 43;
836 ParseInputs AUse = getInputs("A.cppm", CDB);
837 AUse.ModulesManager = &Builder;
838 std::unique_ptr<CompilerInvocation> AInvocation =
840 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));
842 ParseInputs BUse = getInputs(
"B.cppm", CDB);
843 AUse.ModulesManager = &Builder;
844 std::unique_ptr<CompilerInvocation> BInvocation =
846 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));
849 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
851 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
852 EXPECT_TRUE(NewAInfo);
853 EXPECT_TRUE(NewBInfo);
854 HeaderSearchOptions NewHSOptsA(TestDir);
855 HeaderSearchOptions NewHSOptsB(TestDir);
856 NewAInfo->adjustHeaderSearchOptions(NewHSOptsA);
857 NewBInfo->adjustHeaderSearchOptions(NewHSOptsB);
859 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
860 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
862 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
865 EXPECT_TRUE(NewAInfo->canReuse(*AInvocation, FS.view(TestDir)));
866 EXPECT_TRUE(NewBInfo->canReuse(*BInvocation, FS.view(TestDir)));
869TEST_F(PrerequisiteModulesTests, ScanningCacheTest) {
870 MockDirectoryCompilationDatabase CDB(TestDir, FS);
872 CDB.addFile(
"M.cppm", R
"cpp(
875 CDB.addFile("A.cppm", R
"cpp(
879 CDB.addFile("B.cppm", R
"cpp(
884 ModulesBuilder Builder(CDB);
886 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
887 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
888 EXPECT_EQ(CDB.getGlobalScanningCount(), 1u);
894TEST_F(PrerequisiteModulesTests, CanReuseWithHeadersInModuleUnit) {
895 MockDirectoryCompilationDatabase CDB(TestDir, FS);
898 CDB.addFile(
"header1.h", R
"cpp(
899inline int getValue() { return 42; }
903 CDB.addFile(
"M.cppm", R
"cpp(
907export int m_value = getValue();
911 CDB.addFile(
"N.cppm", R
"cpp(
918 CDB.addFile(
"N-part.cppm", R
"cpp(
922 ModulesBuilder Builder(CDB);
925 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
928 ParseInputs NInput = getInputs(
"N.cppm", CDB);
929 std::unique_ptr<CompilerInvocation> Invocation =
933 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
937 CDB.addFile(
"header1.h", R
"cpp(
938inline int getValue() { return 43; }
940 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
943 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
945 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
948 CDB.addFile(
"M.cppm", R
"cpp(
952export int m_value = getValue();
953export int m_new_value = 10;
955 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
958 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
960 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
963TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileTest) {
964 MockDirectoryCompilationDatabase CDB(TestDir, FS);
966 CDB.addFile(
"M.cppm", R
"cpp(
970 CDB.addFile("U.cpp", R
"cpp(
975 ModulesBuilder Builder(CDB);
977 Builder.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
978 HeaderSearchOptions HS(TestDir);
979 ModuleInfo->adjustHeaderSearchOptions(HS);
981 CDB.ExtraClangFlags.push_back(
"-fmodule-file=M=" +
982 HS.PrebuiltModuleFiles[
"M"]);
983 ModulesBuilder Builder2(CDB);
985 Builder2.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
986 HeaderSearchOptions HS2(TestDir);
987 ModuleInfo2->adjustHeaderSearchOptions(HS2);
989 EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles);
996TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileWithRelativePath) {
997 MockDirectoryCompilationDatabase CDB(TestDir, FS);
999 CDB.addFile(
"M.cppm", R
"cpp(
1001export int m_value = 42;
1004 CDB.addFile("U.cpp", R
"cpp(
1006int use() { return m_value; }
1010 ModulesBuilder Builder(CDB);
1012 Builder.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
1013 ASSERT_TRUE(ModuleInfo);
1015 HeaderSearchOptions HS(TestDir);
1016 ModuleInfo->adjustHeaderSearchOptions(HS);
1017 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1020 std::string OriginalBMPath = HS.PrebuiltModuleFiles[
"M"];
1021 ASSERT_TRUE(llvm::sys::path::is_absolute(OriginalBMPath));
1022 ASSERT_TRUE(llvm::sys::fs::exists(OriginalBMPath));
1025 SmallString<256> BMSubDir(TestDir);
1026 llvm::sys::path::append(BMSubDir,
"prebuilt_modules");
1027 ASSERT_FALSE(llvm::sys::fs::create_directories(BMSubDir));
1029 SmallString<256> NewBMPath(BMSubDir);
1030 llvm::sys::path::append(NewBMPath,
"M.pcm");
1033 ASSERT_FALSE(llvm::sys::fs::copy_file(OriginalBMPath, NewBMPath));
1034 ASSERT_TRUE(llvm::sys::fs::exists(NewBMPath));
1037 std::string RelativeBMPath =
1038 llvm::StringRef(NewBMPath).drop_front(TestDir.size() + 1).str();
1039 ASSERT_FALSE(RelativeBMPath.empty());
1040 ASSERT_TRUE(llvm::sys::path::is_relative(RelativeBMPath));
1043 MockDirectoryCompilationDatabase CDBWithRelativePath(TestDir, FS);
1045 CDBWithRelativePath.addFile(
"M.cppm", R
"cpp(
1047export int m_value = 42;
1050 CDBWithRelativePath.addFile("U.cpp", R
"cpp(
1052int use() { return m_value; }
1056 CDBWithRelativePath.ExtraClangFlags.push_back(
"-fmodule-file=M=" +
1060 ModulesBuilder BuilderWithRelativePath(CDBWithRelativePath);
1061 auto ModuleInfo2 = BuilderWithRelativePath.buildPrerequisiteModulesFor(
1062 getFullPath(
"U.cpp"), FS);
1063 ASSERT_TRUE(ModuleInfo2);
1065 HeaderSearchOptions HS2(TestDir);
1066 ModuleInfo2->adjustHeaderSearchOptions(HS2);
1069 ASSERT_EQ(HS2.PrebuiltModuleFiles.count(
"M"), 1u);
1070 EXPECT_EQ(HS2.PrebuiltModuleFiles[
"M"], std::string(NewBMPath))
1071 <<
"Expected absolute path: " << NewBMPath
1072 <<
"\nGot: " << HS2.PrebuiltModuleFiles[
"M"]
1073 <<
"\nRelative path used: " << RelativeBMPath;
1076TEST_F(PrerequisiteModulesTests,
1077 UniqueModuleNameStateResolvedFromCompileCommands) {
1078 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1080 SmallString<256> MPcm(TestDir);
1081 llvm::sys::path::append(MPcm,
"build",
"M.pcm");
1083 CDB.addFile(
"M.cppm", R
"cpp(
1085export int value = 1;
1087 {"--precompile",
"-o", std::string(MPcm)});
1088 CDB.addFile(
"A.cpp", R
"cpp(
1090int useA() { return value; }
1092 {"-fmodule-file=M=" + std::string(MPcm)});
1093 CDB.addFile(
"B.cpp", R
"cpp(
1095int useB() { return value; }
1097 {"-fmodule-file=M=" + std::string(MPcm)});
1099 auto ProjectModules = CDB.getProjectModules(getFullPath(
"A.cpp"));
1100 ASSERT_TRUE(ProjectModules);
1102 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1106TEST_F(PrerequisiteModulesTests,
1107 DuplicateModuleNamesResolvedFromCompileCommands) {
1108 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1110 SmallString<256> APcm(TestDir);
1111 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1112 SmallString<256> BPcm(TestDir);
1113 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1115 CDB.addFile(
"a/M.cppm", R
"cpp(
1117export int onlyA = 1;
1119 {"--precompile",
"-o", std::string(APcm)});
1120 CDB.addFile(
"b/M.cppm", R
"cpp(
1122export int onlyB = 2;
1124 {"--precompile",
"-o", std::string(BPcm)});
1125 CDB.addFile(
"a/Use.cpp", R
"cpp(
1127int useA() { return onlyA; }
1129 {"-fmodule-file=M=" + std::string(APcm)});
1130 CDB.addFile(
"b/Use.cpp", R
"cpp(
1132int useB() { return onlyB; }
1134 {"-fmodule-file=M=" + std::string(BPcm)});
1136 auto ProjectModules = CDB.getProjectModules(getFullPath(
"a/Use.cpp"));
1137 ASSERT_TRUE(ProjectModules);
1138 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1142 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"a/Use.cpp")),
1143 getFullPath(
"a/M.cppm"));
1145 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"b/Use.cpp")),
1146 getFullPath(
"b/M.cppm"));
1149TEST_F(PrerequisiteModulesTests,
1150 DuplicateModuleNamesResolvedFromResponseFiles) {
1151 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1153 SmallString<256> APcm(TestDir);
1154 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1155 SmallString<256> BPcm(TestDir);
1156 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1158 SmallString<256> RspDir(TestDir);
1159 llvm::sys::path::append(RspDir,
"build",
"rsp");
1160 ASSERT_FALSE(llvm::sys::fs::create_directories(RspDir));
1162 SmallString<256> AMRsp(RspDir);
1163 llvm::sys::path::append(AMRsp,
"a-m.rsp");
1166 llvm::raw_fd_ostream OS(AMRsp, EC);
1168 OS <<
"-x c++-module -fmodule-output=" << APcm;
1172 SmallString<256> BMRsp(RspDir);
1173 llvm::sys::path::append(BMRsp,
"b-m.rsp");
1176 llvm::raw_fd_ostream OS(BMRsp, EC);
1178 OS <<
"-x c++-module -fmodule-output=" << BPcm;
1182 SmallString<256> AUseRsp(RspDir);
1183 llvm::sys::path::append(AUseRsp,
"a-use.rsp");
1186 llvm::raw_fd_ostream OS(AUseRsp, EC);
1188 OS <<
"-fmodule-file=M=" << APcm;
1192 SmallString<256> BUseRsp(RspDir);
1193 llvm::sys::path::append(BUseRsp,
"b-use.rsp");
1196 llvm::raw_fd_ostream OS(BUseRsp, EC);
1198 OS <<
"-fmodule-file=M=" << BPcm;
1202 CDB.addFile(
"a/M.cppm", R
"cpp(
1204export int onlyA = 1;
1206 {"@" + std::string(AMRsp)});
1207 CDB.addFile(
"b/M.cppm", R
"cpp(
1209export int onlyB = 2;
1211 {"@" + std::string(BMRsp)});
1212 CDB.addFile(
"a/Use.cpp", R
"cpp(
1214int useA() { return onlyA; }
1216 {"@" + std::string(AUseRsp)});
1217 CDB.addFile(
"b/Use.cpp", R
"cpp(
1219int useB() { return onlyB; }
1221 {"@" + std::string(BUseRsp)});
1223 auto ProjectModules = CDB.getProjectModules(getFullPath(
"a/Use.cpp"));
1224 ASSERT_TRUE(ProjectModules);
1225 EXPECT_EQ(ProjectModules->getModuleNameState(
"M"),
1229 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"a/Use.cpp")),
1230 getFullPath(
"a/M.cppm"));
1232 ProjectModules->getSourceForModuleName(
"M", getFullPath(
"b/Use.cpp")),
1233 getFullPath(
"b/M.cppm"));
1236TEST_F(PrerequisiteModulesTests, DuplicateModuleNamesKeepSeparateBMICache) {
1237 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1239 SmallString<256> APcm(TestDir);
1240 llvm::sys::path::append(APcm,
"build",
"a",
"M.pcm");
1241 SmallString<256> BPcm(TestDir);
1242 llvm::sys::path::append(BPcm,
"build",
"b",
"M.pcm");
1244 CDB.addFile(
"a/M.cppm", R
"cpp(
1246export int onlyA = 1;
1248 {"--precompile",
"-o", std::string(APcm)});
1249 CDB.addFile(
"b/M.cppm", R
"cpp(
1251export int onlyB = 2;
1253 {"--precompile",
"-o", std::string(BPcm)});
1254 CDB.addFile(
"a/Use.cpp", R
"cpp(
1256int useA() { return onlyA; }
1258 {"-fmodule-file=M=" + std::string(APcm)});
1259 CDB.addFile(
"b/Use.cpp", R
"cpp(
1261int useB() { return onlyB; }
1263 {"-fmodule-file=M=" + std::string(BPcm)});
1265 ModulesBuilder Builder(CDB);
1268 Builder.buildPrerequisiteModulesFor(getFullPath(
"a/Use.cpp"), FS);
1270 Builder.buildPrerequisiteModulesFor(getFullPath(
"b/Use.cpp"), FS);
1274 HeaderSearchOptions HSA(TestDir);
1275 HeaderSearchOptions HSB(TestDir);
1276 AInfo->adjustHeaderSearchOptions(HSA);
1277 BInfo->adjustHeaderSearchOptions(HSB);
1278 ASSERT_EQ(HSA.PrebuiltModuleFiles.count(
"M"), 1u);
1279 ASSERT_EQ(HSB.PrebuiltModuleFiles.count(
"M"), 1u);
1280 EXPECT_NE(HSA.PrebuiltModuleFiles[
"M"], HSB.PrebuiltModuleFiles[
"M"]);
1282 auto UseA = getInputs(
"a/Use.cpp", CDB);
1283 UseA.ModulesManager = &Builder;
1286 auto PreambleA =
buildPreamble(getFullPath(
"a/Use.cpp"), *CIA, UseA,
1288 ASSERT_TRUE(PreambleA);
1289 auto ASTA =
ParsedAST::build(getFullPath(
"a/Use.cpp"), UseA, std::move(CIA),
1292 EXPECT_TRUE(
findDecl(*ASTA,
"onlyA").isFromASTFile());
1294 auto UseB = getInputs(
"b/Use.cpp", CDB);
1295 UseB.ModulesManager = &Builder;
1298 auto PreambleB =
buildPreamble(getFullPath(
"b/Use.cpp"), *CIB, UseB,
1300 ASSERT_TRUE(PreambleB);
1301 auto ASTB =
ParsedAST::build(getFullPath(
"b/Use.cpp"), UseB, std::move(CIB),
1304 EXPECT_TRUE(
findDecl(*ASTB,
"onlyB").isFromASTFile());
1307TEST_F(PrerequisiteModulesTests, PersistentModuleCacheReusedAcrossBuilders) {
1308 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1310 CDB.addFile(
"M.cppm", R
"cpp(
1312export int MValue = 43;
1314 CDB.addFile("A.cppm", R
"cpp(
1317export int AValue = MValue;
1320 std::string FirstPCMPath;
1322 ModulesBuilder Builder(CDB);
1323 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1325 HeaderSearchOptions HS(TestDir);
1326 AInfo->adjustHeaderSearchOptions(HS);
1327 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1328 FirstPCMPath = HS.PrebuiltModuleFiles[
"M"];
1329 EXPECT_TRUE(llvm::sys::fs::exists(FirstPCMPath));
1330 EXPECT_TRUE(StringRef(FirstPCMPath).contains(
".cache/clangd/modules"));
1333 EXPECT_FALSE(llvm::sys::fs::exists(FirstPCMPath));
1337 ModulesBuilder Builder(CDB);
1338 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1340 HeaderSearchOptions HS(TestDir);
1341 AInfo->adjustHeaderSearchOptions(HS);
1342 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1343 EXPECT_TRUE(llvm::sys::fs::exists(HS.PrebuiltModuleFiles[
"M"]));
1344 EXPECT_TRUE(StringRef(HS.PrebuiltModuleFiles[
"M"]).contains(
"M-"));
1346 ParseInputs AUse = getInputs(
"A.cppm", CDB);
1347 AUse.ModulesManager = &Builder;
1349 ASSERT_TRUE(Invocation);
1350 EXPECT_TRUE(AInfo->canReuse(*Invocation, FS.view(TestDir)));
1353TEST_F(PrerequisiteModulesTests,
1354 PersistentModuleCacheRebuildsAfterDeletingStalePCM) {
1355 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1357 CDB.addFile(
"M.cppm", R
"cpp(
1359export int MValue = 43;
1361 CDB.addFile("A.cppm", R
"cpp(
1364export int AValue = MValue;
1367 ModulesBuilder Builder(CDB);
1368 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1370 HeaderSearchOptions HS(TestDir);
1371 AInfo->adjustHeaderSearchOptions(HS);
1372 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1373 std::string PCMPath = HS.PrebuiltModuleFiles[
"M"];
1376 llvm::raw_fd_ostream OS(PCMPath, EC);
1383 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1384 ASSERT_TRUE(NewAInfo);
1385 HeaderSearchOptions NewHS(TestDir);
1386 NewAInfo->adjustHeaderSearchOptions(NewHS);
1387 ASSERT_EQ(NewHS.PrebuiltModuleFiles.count(
"M"), 1u);
1388 EXPECT_TRUE(llvm::sys::fs::exists(NewHS.PrebuiltModuleFiles[
"M"]));
1389 EXPECT_TRUE(StringRef(NewHS.PrebuiltModuleFiles[
"M"]).contains(
"M-"));
1391 ParseInputs AUse = getInputs(
"A.cppm", CDB);
1392 AUse.ModulesManager = &Builder;
1394 ASSERT_TRUE(Invocation);
1395 EXPECT_TRUE(NewAInfo->canReuse(*Invocation, FS.view(TestDir)));
1398TEST_F(PrerequisiteModulesTests, PersistentModuleCacheCreatesSourceHashLock) {
1399 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1401 CDB.addFile(
"M.cppm", R
"cpp(
1403export int MValue = 43;
1405 CDB.addFile("A.cppm", R
"cpp(
1408export int AValue = MValue;
1411 ModulesBuilder Builder(CDB);
1412 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1415 HeaderSearchOptions HS(TestDir);
1416 AInfo->adjustHeaderSearchOptions(HS);
1417 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1419 llvm::SmallString<256> PCMPath(HS.PrebuiltModuleFiles[
"M"]);
1420 llvm::sys::path::remove_filename(PCMPath);
1421 llvm::SmallString<256> SourceHashDir(PCMPath);
1422 llvm::sys::path::remove_filename(SourceHashDir);
1423 llvm::SmallString<256> CacheRoot(SourceHashDir);
1424 llvm::sys::path::remove_filename(CacheRoot);
1428 llvm::StringRef SourceDirectoryName =
1429 llvm::sys::path::filename(SourceHashDir);
1431 llvm::StringRef SourceHash = SourceDirectoryName.rsplit(
'-').second;
1432 llvm::SmallString<256> LockPath(CacheRoot);
1433 llvm::sys::path::append(LockPath,
".locks", SourceHash);
1435 EXPECT_TRUE(llvm::sys::fs::exists(LockPath));
1438TEST_F(PrerequisiteModulesTests,
1439 PersistentModuleCacheGCRemovesOldStablePublishedModule) {
1440 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1442 CDB.addFile(
"M.cppm", R
"cpp(
1444export int MValue = 43;
1446 CDB.addFile("A.cppm", R
"cpp(
1449export int AValue = MValue;
1452 llvm::SmallString<256> OrphanPCMPath;
1454 ModulesBuilder Builder(CDB);
1455 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1457 HeaderSearchOptions HS(TestDir);
1458 AInfo->adjustHeaderSearchOptions(HS);
1459 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1461 OrphanPCMPath = HS.PrebuiltModuleFiles[
"M"];
1462 llvm::sys::path::remove_filename(OrphanPCMPath);
1463 llvm::sys::path::append(OrphanPCMPath,
"Orphan.pcm");
1466 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1470 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1473 ASSERT_FALSE(llvm::sys::fs::openFileForWrite(OrphanPCMPath, FD,
1474 llvm::sys::fs::CD_OpenExisting,
1475 llvm::sys::fs::OF_None));
1476 auto CloseFD = llvm::scope_exit(
1477 [&] { llvm::sys::Process::SafelyCloseFileDescriptor(FD); });
1478 llvm::sys::TimePoint<> OldTime =
1479 std::chrono::system_clock::now() - std::chrono::hours(24 * 5);
1480 ASSERT_FALSE(llvm::sys::fs::setLastAccessAndModificationTime(FD, OldTime));
1483 ModulesBuilder Builder(CDB);
1484 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1486 EXPECT_FALSE(llvm::sys::fs::exists(OrphanPCMPath));
1489TEST_F(PrerequisiteModulesTests,
1490 PersistentModuleCacheGCKeepsRecentStablePublishedModule) {
1491 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1493 CDB.addFile(
"M.cppm", R
"cpp(
1495export int MValue = 43;
1497 CDB.addFile("A.cppm", R
"cpp(
1500export int AValue = MValue;
1503 llvm::SmallString<256> OrphanPCMPath;
1505 ModulesBuilder Builder(CDB);
1506 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1508 HeaderSearchOptions HS(TestDir);
1509 AInfo->adjustHeaderSearchOptions(HS);
1510 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1512 OrphanPCMPath = HS.PrebuiltModuleFiles[
"M"];
1513 llvm::sys::path::remove_filename(OrphanPCMPath);
1514 llvm::sys::path::append(OrphanPCMPath,
"Orphan.pcm");
1517 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1521 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1524 ModulesBuilder Builder(CDB);
1525 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1527 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1530TEST_F(PrerequisiteModulesTests,
1531 PersistentModuleCacheGCRemovesOldVersionedModuleFile) {
1532 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1534 CDB.addFile(
"M.cppm", R
"cpp(
1536export int MValue = 43;
1538 CDB.addFile("A.cppm", R
"cpp(
1541export int AValue = MValue;
1544 llvm::SmallString<256> OldVersionedPCMPath;
1546 ModulesBuilder Builder(CDB);
1547 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1549 HeaderSearchOptions HS(TestDir);
1550 AInfo->adjustHeaderSearchOptions(HS);
1551 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1553 OldVersionedPCMPath = HS.PrebuiltModuleFiles[
"M"];
1554 ASSERT_TRUE(llvm::sys::fs::exists(OldVersionedPCMPath));
1557 ASSERT_FALSE(llvm::sys::fs::openFileForWrite(OldVersionedPCMPath, FD,
1558 llvm::sys::fs::CD_OpenExisting,
1559 llvm::sys::fs::OF_None));
1560 auto CloseFD = llvm::scope_exit(
1561 [&] { llvm::sys::Process::SafelyCloseFileDescriptor(FD); });
1562 llvm::sys::TimePoint<> OldTime =
1563 std::chrono::system_clock::now() - std::chrono::hours(24 * 5);
1564 ASSERT_FALSE(llvm::sys::fs::setLastAccessAndModificationTime(FD, OldTime));
1567 ModulesBuilder Builder(CDB);
1568 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1570 EXPECT_FALSE(llvm::sys::fs::exists(OldVersionedPCMPath));
1573TEST_F(PrerequisiteModulesTests,
1574 PersistentModuleCacheGCKeepsRecentVersionedModuleFile) {
1575 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1577 CDB.addFile(
"M.cppm", R
"cpp(
1579export int MValue = 43;
1581 CDB.addFile("A.cppm", R
"cpp(
1584export int AValue = MValue;
1587 auto FirstBuilder = std::make_unique<ModulesBuilder>(CDB);
1589 FirstBuilder->buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1591 HeaderSearchOptions HS(TestDir);
1592 AInfo->adjustHeaderSearchOptions(HS);
1593 ASSERT_EQ(HS.PrebuiltModuleFiles.count(
"M"), 1u);
1594 llvm::StringRef CopyOnReadPCMPath = HS.PrebuiltModuleFiles[
"M"];
1595 ASSERT_TRUE(llvm::sys::fs::exists(CopyOnReadPCMPath));
1597 ModulesBuilder SecondBuilder(CDB);
1599 SecondBuilder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
1600 ASSERT_TRUE(SecondInfo);
1601 EXPECT_TRUE(llvm::sys::fs::exists(CopyOnReadPCMPath));
1604TEST_F(PrerequisiteModulesTests,
1605 PersistentModuleCacheIgnoresRequiredSourceForOnDiskPath) {
1606 ModuleUnitRootCompilationDatabase CDB(TestDir, FS);
1608 CDB.addFile(
"shared/M.cppm", R
"cpp(
1610export int MValue = 43;
1612 CDB.addFile("projA/A.cppm", R
"cpp(
1615export int AValue = MValue;
1617 CDB.addFile("projB/B.cppm", R
"cpp(
1620export int BValue = MValue;
1623 ModulesBuilder Builder(CDB);
1626 Builder.buildPrerequisiteModulesFor(getFullPath(
"projA/A.cppm"), FS);
1628 Builder.buildPrerequisiteModulesFor(getFullPath(
"projB/B.cppm"), FS);
1632 HeaderSearchOptions HSA(TestDir);
1633 HeaderSearchOptions HSB(TestDir);
1634 AInfo->adjustHeaderSearchOptions(HSA);
1635 BInfo->adjustHeaderSearchOptions(HSB);
1636 ASSERT_EQ(HSA.PrebuiltModuleFiles.count(
"M"), 1u);
1637 ASSERT_EQ(HSB.PrebuiltModuleFiles.count(
"M"), 1u);
1638 EXPECT_TRUE(llvm::sys::fs::exists(HSA.PrebuiltModuleFiles[
"M"]));
1639 EXPECT_TRUE(llvm::sys::fs::exists(HSB.PrebuiltModuleFiles[
"M"]));
1641 llvm::SmallString<256> ExpectedRoot(getFullPath(
"shared/M.cppm"));
1642 llvm::sys::path::remove_filename(ExpectedRoot);
1643 llvm::sys::path::append(ExpectedRoot,
".cache",
"clangd",
"modules");
1645 StringRef(HSA.PrebuiltModuleFiles[
"M"]).starts_with(ExpectedRoot));
1646 EXPECT_TRUE(StringRef(HSA.PrebuiltModuleFiles[
"M"]).contains(
"M.cppm-"));
1648 StringRef(HSB.PrebuiltModuleFiles[
"M"]).starts_with(ExpectedRoot));
1649 EXPECT_TRUE(StringRef(HSB.PrebuiltModuleFiles[
"M"]).contains(
"M.cppm-"));
1652TEST_F(PrerequisiteModulesTests, ModuleImportThroughInclude) {
1653 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1655 Annotations UseCpp(R
"cpp(
1656#include "Header.hpp"
1663 CDB.addFile("M.cppm", R
"cpp(
1665export struct TypeFromModule {};
1668 CDB.addFile("Header.hpp", R
"cpp(
1670struct TypeFromHeader {};
1673 CDB.addFile("Use.cpp", UseCpp.code());
1675 ModulesBuilder Builder(CDB);
1677 auto Inputs = getInputs(
"Use.cpp", CDB);
1678 Inputs.ModulesManager = &Builder;
1679 Inputs.Opts.SkipPreambleBuild =
true;
1688 EXPECT_EQ(
Preamble->Preamble.getBounds().Size, 0u);
1694 auto Result =
codeComplete(getFullPath(
"Use.cpp"), UseCpp.point(),
1696 EXPECT_THAT(Result.Completions,
1697 testing::UnorderedElementsAre(named(
"TypeFromModule"),
1698 named(
"TypeFromHeader")));
1701TEST_F(PrerequisiteModulesTests,
1702 SkipPreambleBuildInvalidatedByNewModuleImport) {
1703 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1705 CDB.addFile(
"Dep.cppm", R
"cpp(
1709 CDB.addFile("Consumer.cpp", R
"cpp(
1714 ModulesBuilder Builder(CDB);
1716 auto Inputs = getInputs(
"Consumer.cpp", CDB);
1717 Inputs.ModulesManager = &Builder;
1718 Inputs.Opts.SkipPreambleBuild =
true;
1727 EXPECT_EQ(
Preamble->Preamble.getBounds().Size, 0u);
1728 ASSERT_TRUE(
Preamble->RequiredModules);
1730 CDB.addFile(
"NewDep.cppm", R
"cpp(
1731export module NewDep;
1735 Inputs.Contents = R
"cpp(
1743 llvm::raw_fd_ostream OS(getFullPath("Consumer.cpp"), EC,
1744 llvm::sys::fs::OF_None);
1746 OS << Inputs.Contents;
1753 getFullPath(
"Consumer.cpp"), *NewCI));
1756TEST_F(PrerequisiteModulesTests, ModuleSemanticHighlighting) {
1757 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1759 llvm::StringRef AnnotatedCode = R
"cpp(
1761 $import[[import]] M;
1762 export module highlight;
1763 $export[[export]] void foo() {
1766 Annotations UseCpp(AnnotatedCode);
1768 CDB.addFile("M.cppm", R
"cpp(
1770export struct TypeFromModule {};
1773 CDB.addFile("Use.cpp", UseCpp.code());
1775 ModulesBuilder Builder(CDB);
1777 auto Inputs = getInputs(
"Use.cpp", CDB);
1778 Inputs.ModulesManager = &Builder;
1779 Inputs.Opts.SkipPreambleBuild =
true;
1797 return llvm::any_of(Actual, [&](
const HighlightingToken &T) {
1798 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.
@ Info
An information message.
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::vector< LocatedSymbol > locateSymbolAt(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Get definition of symbol at a specified Pos.
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.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.