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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp