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"
32 GlobalScanningCounterProjectModules(
33 std::unique_ptr<ProjectModules> Underlying, std::atomic<unsigned> &Count)
34 : Underlying(std::move(Underlying)), Count(Count) {}
36 std::vector<std::string> getRequiredModules(
PathRef File)
override {
37 return Underlying->getRequiredModules(
File);
40 std::string getModuleNameForSource(
PathRef File)
override {
41 return Underlying->getModuleNameForSource(
File);
44 void setCommandMangler(CommandMangler Mangler)
override {
45 Underlying->setCommandMangler(std::move(Mangler));
48 std::string getSourceForModuleName(llvm::StringRef ModuleName,
49 PathRef RequiredSrcFile)
override {
51 return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile);
55 std::unique_ptr<ProjectModules> Underlying;
56 std::atomic<unsigned> &Count;
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");
69 void addFile(llvm::StringRef
Path, llvm::StringRef Contents);
71 std::unique_ptr<ProjectModules> getProjectModules(
PathRef)
const override {
72 return std::make_unique<GlobalScanningCounterProjectModules>(
76 unsigned getGlobalScanningCount()
const {
return GlobalScanningCount; }
79 class MockClangCompilationDatabase :
public tooling::CompilationDatabase {
81 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
84 std::vector<tooling::CompileCommand>
85 getCompileCommands(StringRef FilePath)
const override {
86 std::optional<tooling::CompileCommand> Cmd =
87 MCDB.getCompileCommand(FilePath);
92 std::vector<std::string> getAllFiles()
const override {
return Files; }
94 void AddFile(StringRef
File) { Files.push_back(
File.str()); }
97 MockDirectoryCompilationDatabase &MCDB;
98 std::vector<std::string> Files;
101 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
102 const ThreadsafeFS &TFS;
104 mutable std::atomic<unsigned> GlobalScanningCount;
108void MockDirectoryCompilationDatabase::addFile(llvm::StringRef
Path,
109 llvm::StringRef Contents) {
110 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
113 llvm::sys::path::append(AbsPath, Path);
116 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
119 llvm::raw_fd_ostream OS(AbsPath, EC);
123 MockedCDBPtr->AddFile(Path);
126class PrerequisiteModulesTests :
public ::testing::Test {
128 void SetUp()
override {
129 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
"modules-test", TestDir));
132 void TearDown()
override {
133 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
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();
146 ParseInputs getInputs(llvm::StringRef FileName,
147 const GlobalCompilationDatabase &CDB) {
148 std::string FullPathName = getFullPath(FileName);
151 std::optional<tooling::CompileCommand> Cmd =
152 CDB.getCompileCommand(FullPathName);
154 Inputs.CompileCommand = std::move(*Cmd);
157 if (
auto Contents = FS.
view(TestDir)->getBufferForFile(FullPathName))
158 Inputs.Contents = Contents->get()->getBuffer().str();
163 SmallString<256> TestDir;
168 DiagnosticConsumer DiagConsumer;
171TEST_F(PrerequisiteModulesTests, NonModularTest) {
172 MockDirectoryCompilationDatabase CDB(TestDir, FS);
174 CDB.addFile(
"foo.h", R
"cpp(
178 CDB.addFile("NonModular.cpp", R
"cpp(
185 ModulesBuilder Builder(CDB);
188 auto NonModularInfo =
189 Builder.buildPrerequisiteModulesFor(getFullPath(
"NonModular.cpp"), FS);
190 EXPECT_TRUE(NonModularInfo);
192 HeaderSearchOptions HSOpts;
193 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
194 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
198 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));
201TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
202 MockDirectoryCompilationDatabase CDB(TestDir, FS);
204 CDB.addFile(
"foo.h", R
"cpp(
208 CDB.addFile("M.cppm", R
"cpp(
214 ModulesBuilder Builder(CDB);
216 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"M.cppm"), FS);
220 HeaderSearchOptions HSOpts;
221 MInfo->adjustHeaderSearchOptions(HSOpts);
222 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
226 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
229TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
230 MockDirectoryCompilationDatabase CDB(TestDir, FS);
232 CDB.ExtraClangFlags.push_back(
"-invalid-unknown-flag");
234 CDB.addFile(
"Dep.cppm", R
"cpp(
238 CDB.addFile("M.cppm", R
"cpp(
245 auto ProjectModules = CDB.getProjectModules(getFullPath(
"M.cppm"));
247 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
250 ProjectModules->setCommandMangler([](tooling::CompileCommand &Command,
252 auto const It = llvm::find(Command.CommandLine,
"-invalid-unknown-flag");
253 Command.CommandLine.erase(It);
259 ProjectModules->getRequiredModules(getFullPath(
"M.cppm")).empty());
262TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
263 MockDirectoryCompilationDatabase CDB(TestDir, FS);
265 CDB.addFile(
"foo.h", R
"cpp(
269 CDB.addFile("M.cppm", R
"cpp(
275 CDB.addFile("N.cppm", R
"cpp(
281 CDB.addFile("N-part.cppm", R
"cpp(
282// Different module name with filename intentionally.
286 ModulesBuilder Builder(CDB);
288 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
291 ParseInputs NInput = getInputs(
"N.cppm", CDB);
292 std::unique_ptr<CompilerInvocation> Invocation =
295 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
301 HeaderSearchOptions HSOpts;
302 NInfo->adjustHeaderSearchOptions(HSOpts);
304 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"M"));
305 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count(
"N:Part"));
312 HeaderSearchOptions HSOpts;
313 HSOpts.PrebuiltModuleFiles[
"M"] =
"incorrect_path";
314 HSOpts.PrebuiltModuleFiles[
"N:Part"] =
"incorrect_path";
315 NInfo->adjustHeaderSearchOptions(HSOpts);
317 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles[
"M"]).ends_with(
".pcm"));
319 StringRef(HSOpts.PrebuiltModuleFiles[
"N:Part"]).ends_with(
".pcm"));
323TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
324 MockDirectoryCompilationDatabase CDB(TestDir, FS);
326 CDB.addFile(
"foo.h", R
"cpp(
330 CDB.addFile("M.cppm", R
"cpp(
336 CDB.addFile("N.cppm", R
"cpp(
342 CDB.addFile("N-part.cppm", R
"cpp(
343// Different module name with filename intentionally.
347 ModulesBuilder Builder(CDB);
349 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"N.cppm"), FS);
353 ParseInputs NInput = getInputs(
"N.cppm", CDB);
354 std::unique_ptr<CompilerInvocation> Invocation =
356 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
360 CDB.addFile(
"L.cppm", R
"cpp(
366 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
368 CDB.addFile("bar.h", R
"cpp(
370inline void bar(int) {}
372 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
377 CDB.addFile(
"M.cppm", R
"cpp(
383 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
385 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
386 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
388 CDB.addFile(
"foo.h", R
"cpp(
390inline void foo(int) {}
392 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
394 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
395 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
398 CDB.addFile(
"N-part.cppm", R
"cpp(
400// Intentioned to make it uncompilable.
401export int NPart = 4LIdjwldijaw
403 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
404 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
406 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
408 CDB.addFile(
"N-part.cppm", R
"cpp(
410export int NPart = 43;
413 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
414 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
416 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
420 CDB.addFile(
"N-part.cppm", R
"cpp(
422export int NPart = 43;
424 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
426 CDB.addFile("N.cppm", R
"cpp(
434 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
438TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
439 MockDirectoryCompilationDatabase CDB(TestDir, FS);
441 CDB.addFile(
"A.cppm", R
"cpp(
446 CDB.addFile("Use.cpp", R
"cpp(
450 ModulesBuilder Builder(CDB);
452 ParseInputs Use = getInputs("Use.cpp", CDB);
453 Use.ModulesManager = &Builder;
455 std::unique_ptr<CompilerInvocation> CI =
463 EXPECT_TRUE(
Preamble->RequiredModules);
470 EXPECT_TRUE(
D.isFromASTFile());
474TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
475 MockDirectoryCompilationDatabase CDB(TestDir, FS);
477 CDB.addFile(
"A.cppm", R
"cpp(
482 llvm::StringLiteral UserContents = R"cpp(
489 CDB.addFile("Use.cpp", UserContents);
490 Annotations Test(UserContents);
492 ModulesBuilder Builder(CDB);
494 ParseInputs Use = getInputs(
"Use.cpp", CDB);
495 Use.ModulesManager = &Builder;
497 std::unique_ptr<CompilerInvocation> CI =
505 EXPECT_TRUE(
Preamble->RequiredModules);
507 auto Result =
codeComplete(getFullPath(
"Use.cpp"), Test.point(),
509 EXPECT_FALSE(Result.Completions.empty());
510 EXPECT_EQ(Result.Completions[0].Name,
"printA");
513TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
514 MockDirectoryCompilationDatabase CDB(TestDir, FS);
516 CDB.addFile(
"A.cppm", R
"cpp(
518export void printA(int a);
521 llvm::StringLiteral UserContents = R"cpp(
528 CDB.addFile("Use.cpp", UserContents);
529 Annotations Test(UserContents);
531 ModulesBuilder Builder(CDB);
533 ParseInputs Use = getInputs(
"Use.cpp", CDB);
534 Use.ModulesManager = &Builder;
536 std::unique_ptr<CompilerInvocation> CI =
544 EXPECT_TRUE(
Preamble->RequiredModules);
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");
553TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
554 MockDirectoryCompilationDatabase CDB(TestDir, FS);
556 CDB.addFile(
"M.cppm", R
"cpp(
560 CDB.addFile("A.cppm", R
"cpp(
563export int A = 43 + M;
565 CDB.addFile("B.cppm", R
"cpp(
568export int B = 44 + M;
571 ModulesBuilder Builder(CDB);
573 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
575 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
577 HeaderSearchOptions HSOptsA(TestDir);
578 HeaderSearchOptions HSOptsB(TestDir);
579 AInfo->adjustHeaderSearchOptions(HSOptsA);
580 BInfo->adjustHeaderSearchOptions(HSOptsB);
582 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
583 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
586 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
589 CDB.addFile(
"M.cppm", R
"cpp(
591export constexpr int M = 43;
594 ParseInputs AUse = getInputs("A.cppm", CDB);
595 AUse.ModulesManager = &Builder;
596 std::unique_ptr<CompilerInvocation> AInvocation =
598 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));
600 ParseInputs BUse = getInputs(
"B.cppm", CDB);
601 AUse.ModulesManager = &Builder;
602 std::unique_ptr<CompilerInvocation> BInvocation =
604 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));
607 Builder.buildPrerequisiteModulesFor(getFullPath(
"A.cppm"), FS);
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);
617 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
618 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
620 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
622 EXPECT_NE(NewHSOptsA.PrebuiltModuleFiles, HSOptsA.PrebuiltModuleFiles);
625TEST_F(PrerequisiteModulesTests, ScanningCacheTest) {
626 MockDirectoryCompilationDatabase CDB(TestDir, FS);
628 CDB.addFile(
"M.cppm", R
"cpp(
631 CDB.addFile("A.cppm", R
"cpp(
635 CDB.addFile("B.cppm", R
"cpp(
640 ModulesBuilder Builder(CDB);
642 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
643 Builder.buildPrerequisiteModulesFor(getFullPath(
"B.cppm"), FS);
644 EXPECT_EQ(CDB.getGlobalScanningCount(), 1u);
647TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileTest) {
648 MockDirectoryCompilationDatabase CDB(TestDir, FS);
650 CDB.addFile(
"M.cppm", R
"cpp(
654 CDB.addFile("U.cpp", R
"cpp(
659 ModulesBuilder Builder(CDB);
661 Builder.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
662 HeaderSearchOptions HS(TestDir);
663 ModuleInfo->adjustHeaderSearchOptions(HS);
665 CDB.ExtraClangFlags.push_back(
"-fmodule-file=M=" +
666 HS.PrebuiltModuleFiles[
"M"]);
667 ModulesBuilder Builder2(CDB);
669 Builder2.buildPrerequisiteModulesFor(getFullPath(
"U.cpp"), FS);
670 HeaderSearchOptions HS2(TestDir);
671 ModuleInfo2->adjustHeaderSearchOptions(HS2);
673 EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles);
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
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.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
std::unique_ptr< ProjectModules > scanningProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
Providing modules information for the project by scanning every file.
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.
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.