clang-tools 24.0.0git
PrerequisiteModulesTest.cpp
Go to the documentation of this file.
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 "Preamble.h"
19#include "ProjectModules.h"
21#include "TestTU.h"
22#include "support/Path.h"
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"
36
37namespace clang::clangd {
38namespace {
39
40MATCHER_P(named, Name, "") { return arg.Name == Name; }
41
42class GlobalScanningCounterProjectModules : public ProjectModules {
43public:
44 GlobalScanningCounterProjectModules(
45 std::unique_ptr<ProjectModules> Underlying, std::atomic<unsigned> &Count)
46 : Underlying(std::move(Underlying)), Count(Count) {}
47
48 std::vector<std::string> getRequiredModules(PathRef File) override {
49 return Underlying->getRequiredModules(File);
50 }
51
52 std::string getModuleNameForSource(PathRef File) override {
53 return Underlying->getModuleNameForSource(File);
54 }
55
56 void setCommandMangler(CommandMangler Mangler) override {
57 Underlying->setCommandMangler(std::move(Mangler));
58 }
59
60 std::string getSourceForModuleName(llvm::StringRef ModuleName,
61 PathRef RequiredSrcFile) override {
62 Count++;
63 return Underlying->getSourceForModuleName(ModuleName, RequiredSrcFile);
64 }
65
66 ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override {
67 return Underlying->getModuleNameState(ModuleName);
68 }
69
70private:
71 std::unique_ptr<ProjectModules> Underlying;
72 std::atomic<unsigned> &Count;
73};
74
75class PerFileModulesCompilationDatabase : public GlobalCompilationDatabase {
76public:
77 PerFileModulesCompilationDatabase(StringRef TestDir, const ThreadsafeFS &TFS)
78 : Directory(TestDir), TFS(TFS),
79 ToolingCDB(std::make_shared<IndexedCompilationDatabase>(*this)) {}
80
81 void addFile(llvm::StringRef Path, llvm::StringRef Contents,
82 std::vector<std::string> ExtraFlags = {}) {
83 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
84
85 SmallString<256> AbsPath(Directory);
86 llvm::sys::path::append(AbsPath, Path);
87
88 ASSERT_FALSE(llvm::sys::fs::create_directories(
89 llvm::sys::path::parent_path(AbsPath)));
90
91 std::error_code EC;
92 llvm::raw_fd_ostream OS(AbsPath, EC);
93 ASSERT_FALSE(EC);
94 OS << Contents;
95
96 std::vector<std::string> CommandLine = {"clang", "-std=c++20", "-c"};
97 CommandLine.insert(CommandLine.end(), ExtraFlags.begin(), ExtraFlags.end());
98 CommandLine.push_back(std::string(AbsPath));
99
100 Commands[maybeCaseFoldPath(AbsPath)] = tooling::CompileCommand(
101 Directory, std::string(AbsPath), std::move(CommandLine), "");
102 Files.push_back(std::string(AbsPath));
103 }
104
105 std::optional<tooling::CompileCommand>
106 getCompileCommand(PathRef File) const override {
107 auto It = Commands.find(maybeCaseFoldPath(File));
108 if (It == Commands.end())
109 return std::nullopt;
110 tooling::CompileCommand Cmd = It->second;
111 if (llvm::any_of(Cmd.CommandLine, [](llvm::StringRef Arg) {
112 return Arg.starts_with("@");
113 })) {
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,
119 Tokenizer, *FS);
120 }
121 return Cmd;
122 }
123
124 std::optional<ProjectInfo> getProjectInfo(PathRef) const override {
125 return ProjectInfo{std::string(Directory)};
126 }
127
128 std::unique_ptr<ProjectModules> getProjectModules(PathRef) const override {
129 return clang::clangd::getProjectModules(ToolingCDB, TFS);
130 }
131
132private:
133 class IndexedCompilationDatabase : public tooling::CompilationDatabase {
134 public:
135 IndexedCompilationDatabase(const PerFileModulesCompilationDatabase &CDB)
136 : CDB(CDB) {}
137
138 std::vector<tooling::CompileCommand>
139 getCompileCommands(StringRef FilePath) const override {
140 if (auto Cmd = CDB.getCompileCommand(FilePath))
141 return {*Cmd};
142 return {};
143 }
144
145 std::vector<std::string> getAllFiles() const override { return CDB.Files; }
146
147 private:
148 const PerFileModulesCompilationDatabase &CDB;
149 };
150
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;
156};
157
158class ModuleUnitRootCompilationDatabase
159 : public PerFileModulesCompilationDatabase {
160public:
161 ModuleUnitRootCompilationDatabase(StringRef TestDir, const ThreadsafeFS &TFS)
162 : PerFileModulesCompilationDatabase(TestDir, TFS) {}
163
164 std::optional<ProjectInfo> getProjectInfo(PathRef File) const override {
165 // Treat each module-unit directory as its own project root so tests can
166 // verify that the persistent cache follows the providing module unit.
167 llvm::SmallString<256> Root(File);
168 llvm::sys::path::remove_filename(Root);
169 return ProjectInfo{std::string(Root)};
170 }
171};
172
173class MockDirectoryCompilationDatabase : public MockCompilationDatabase {
174public:
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");
181 }
182
183 void addFile(llvm::StringRef Path, llvm::StringRef Contents);
184
185 std::unique_ptr<ProjectModules> getProjectModules(PathRef) const override {
186 return std::make_unique<GlobalScanningCounterProjectModules>(
187 clang::clangd::getProjectModules(MockedCDBPtr, TFS),
188 GlobalScanningCount);
189 }
190
191 unsigned getGlobalScanningCount() const { return GlobalScanningCount; }
192
193private:
194 class MockClangCompilationDatabase : public tooling::CompilationDatabase {
195 public:
196 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
197 : MCDB(MCDB) {}
198
199 std::vector<tooling::CompileCommand>
200 getCompileCommands(StringRef FilePath) const override {
201 std::optional<tooling::CompileCommand> Cmd =
202 MCDB.getCompileCommand(FilePath);
203 EXPECT_TRUE(Cmd);
204 return {*Cmd};
205 }
206
207 std::vector<std::string> getAllFiles() const override { return Files; }
208
209 void AddFile(StringRef File) { Files.push_back(File.str()); }
210
211 private:
212 MockDirectoryCompilationDatabase &MCDB;
213 std::vector<std::string> Files;
214 };
215
216 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
217 const ThreadsafeFS &TFS;
218
219 mutable std::atomic<unsigned> GlobalScanningCount;
220};
221
222// Add files to the working testing directory and the compilation database.
223void MockDirectoryCompilationDatabase::addFile(llvm::StringRef Path,
224 llvm::StringRef Contents) {
225 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
226
227 SmallString<256> AbsPath(Directory);
228 llvm::sys::path::append(AbsPath, Path);
229
230 ASSERT_FALSE(
231 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
232
233 std::error_code EC;
234 llvm::raw_fd_ostream OS(AbsPath, EC);
235 ASSERT_FALSE(EC);
236 OS << Contents;
237
238 MockedCDBPtr->AddFile(Path);
239}
240
241class PrerequisiteModulesTests : public ::testing::Test {
242protected:
243 void SetUp() override {
244 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("modules-test", TestDir));
245 }
246
247 void TearDown() override {
248 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
249 }
250
251public:
252 // Get the absolute path for file specified by Path under testing working
253 // directory.
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();
259 }
260
261 ParseInputs getInputs(llvm::StringRef FileName,
262 const GlobalCompilationDatabase &CDB) {
263 std::string FullPathName = getFullPath(FileName);
264
265 ParseInputs Inputs;
266 std::optional<tooling::CompileCommand> Cmd =
267 CDB.getCompileCommand(FullPathName);
268 EXPECT_TRUE(Cmd);
269 Inputs.CompileCommand = std::move(*Cmd);
270 Inputs.TFS = &FS;
271
272 if (auto Contents = FS.view(TestDir)->getBufferForFile(FullPathName))
273 Inputs.Contents = Contents->get()->getBuffer().str();
274
275 return Inputs;
276 }
277
278 SmallString<256> TestDir;
279 // FIXME: It will be better to use the MockFS if the scanning process and
280 // build module process doesn't depend on reading real IO.
281 RealThreadsafeFS FS;
282
283 DiagnosticConsumer DiagConsumer;
284};
285
286TEST_F(PrerequisiteModulesTests, NonModularTest) {
287 MockDirectoryCompilationDatabase CDB(TestDir, FS);
288
289 CDB.addFile("foo.h", R"cpp(
290inline void foo() {}
291 )cpp");
292
293 CDB.addFile("NonModular.cpp", R"cpp(
294#include "foo.h"
295void use() {
296 foo();
297}
298 )cpp");
299
300 ModulesBuilder Builder(CDB);
301
302 // NonModular.cpp is not related to modules. So nothing should be built.
303 auto NonModularInfo =
304 Builder.buildPrerequisiteModulesFor(getFullPath("NonModular.cpp"), FS);
305 EXPECT_TRUE(NonModularInfo);
306
307 HeaderSearchOptions HSOpts;
308 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
309 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
310
311 auto Invocation =
312 buildCompilerInvocation(getInputs("NonModular.cpp", CDB), DiagConsumer);
313 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));
314}
315
316TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
317 MockDirectoryCompilationDatabase CDB(TestDir, FS);
318
319 CDB.addFile("foo.h", R"cpp(
320inline void foo() {}
321 )cpp");
322
323 CDB.addFile("M.cppm", R"cpp(
324module;
325#include "foo.h"
326export module M;
327 )cpp");
328
329 ModulesBuilder Builder(CDB);
330
331 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath("M.cppm"), FS);
332 EXPECT_TRUE(MInfo);
333
334 // Nothing should be built since M doesn't dependent on anything.
335 HeaderSearchOptions HSOpts;
336 MInfo->adjustHeaderSearchOptions(HSOpts);
337 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
338
339 auto Invocation =
340 buildCompilerInvocation(getInputs("M.cppm", CDB), DiagConsumer);
341 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
342}
343
344TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
345 MockDirectoryCompilationDatabase CDB(TestDir, FS);
346
347 CDB.ExtraClangFlags.push_back("-invalid-unknown-flag");
348
349 CDB.addFile("Dep.cppm", R"cpp(
350export module Dep;
351 )cpp");
352
353 CDB.addFile("M.cppm", R"cpp(
354export module M;
355import Dep;
356 )cpp");
357
358 // An invalid flag will break the module compilation and the
359 // getRequiredModules would return an empty array
360 auto ProjectModules = CDB.getProjectModules(getFullPath("M.cppm"));
361 EXPECT_TRUE(
362 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
363
364 // Set the mangler to filter out the invalid flag
365 ProjectModules->setCommandMangler([](tooling::CompileCommand &Command,
366 PathRef) {
367 auto const It = llvm::find(Command.CommandLine, "-invalid-unknown-flag");
368 Command.CommandLine.erase(It);
369 });
370
371 // And now it returns a non-empty list of required modules since the
372 // compilation succeeded
373 EXPECT_FALSE(
374 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
375}
376
377TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
378 MockDirectoryCompilationDatabase CDB(TestDir, FS);
379
380 CDB.addFile("foo.h", R"cpp(
381inline void foo() {}
382 )cpp");
383
384 CDB.addFile("M.cppm", R"cpp(
385module;
386#include "foo.h"
387export module M;
388 )cpp");
389
390 CDB.addFile("N.cppm", R"cpp(
391export module N;
392import :Part;
393import M;
394 )cpp");
395
396 CDB.addFile("N-part.cppm", R"cpp(
397// Different module name with filename intentionally.
398export module N:Part;
399 )cpp");
400
401 ModulesBuilder Builder(CDB);
402
403 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
404 EXPECT_TRUE(NInfo);
405
406 ParseInputs NInput = getInputs("N.cppm", CDB);
407 std::unique_ptr<CompilerInvocation> Invocation =
408 buildCompilerInvocation(NInput, DiagConsumer);
409 // Test that `PrerequisiteModules::canReuse` works basically.
410 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
411
412 {
413 // Check that
414 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`
415 // can appending HeaderSearchOptions correctly.
416 HeaderSearchOptions HSOpts;
417 NInfo->adjustHeaderSearchOptions(HSOpts);
418
419 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("M"));
420 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("N:Part"));
421 }
422
423 {
424 // Check that
425 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`
426 // can replace HeaderSearchOptions correctly.
427 HeaderSearchOptions HSOpts;
428 HSOpts.PrebuiltModuleFiles["M"] = "incorrect_path";
429 HSOpts.PrebuiltModuleFiles["N:Part"] = "incorrect_path";
430 NInfo->adjustHeaderSearchOptions(HSOpts);
431
432 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles["M"]).ends_with(".pcm"));
433 EXPECT_TRUE(
434 StringRef(HSOpts.PrebuiltModuleFiles["N:Part"]).ends_with(".pcm"));
435 }
436}
437
438TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
439 MockDirectoryCompilationDatabase CDB(TestDir, FS);
440
441 CDB.addFile("foo.h", R"cpp(
442inline void foo() {}
443 )cpp");
444
445 CDB.addFile("M.cppm", R"cpp(
446module;
447#include "foo.h"
448export module M;
449 )cpp");
450
451 CDB.addFile("N.cppm", R"cpp(
452export module N;
453import :Part;
454import M;
455 )cpp");
456
457 CDB.addFile("N-part.cppm", R"cpp(
458// Different module name with filename intentionally.
459export module N:Part;
460 )cpp");
461
462 ModulesBuilder Builder(CDB);
463
464 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
465 EXPECT_TRUE(NInfo);
466 EXPECT_TRUE(NInfo);
467
468 ParseInputs NInput = getInputs("N.cppm", CDB);
469 std::unique_ptr<CompilerInvocation> Invocation =
470 buildCompilerInvocation(NInput, DiagConsumer);
471 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
472
473 // Test that we can still reuse the NInfo after we touch a unrelated file.
474 {
475 CDB.addFile("L.cppm", R"cpp(
476module;
477#include "foo.h"
478export module L;
479export int ll = 43;
480 )cpp");
481 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
482
483 CDB.addFile("bar.h", R"cpp(
484inline void bar() {}
485inline void bar(int) {}
486 )cpp");
487 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
488 }
489
490 // Test that we can't reuse the NInfo after we touch a related file.
491 {
492 CDB.addFile("M.cppm", R"cpp(
493module;
494#include "foo.h"
495export module M;
496export int mm = 44;
497 )cpp");
498 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
499
500 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
501 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
502
503 CDB.addFile("foo.h", R"cpp(
504inline void foo() {}
505inline void foo(int) {}
506 )cpp");
507 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
508
509 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
510 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
511 }
512
513 CDB.addFile("N-part.cppm", R"cpp(
514export module N:Part;
515// Intentioned to make it uncompilable.
516export int NPart = 4LIdjwldijaw
517 )cpp");
518 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
519 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
520 EXPECT_TRUE(NInfo);
521 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
522
523 CDB.addFile("N-part.cppm", R"cpp(
524export module N:Part;
525export int NPart = 43;
526 )cpp");
527 EXPECT_TRUE(NInfo);
528 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
529 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
530 EXPECT_TRUE(NInfo);
531 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
532
533 // Test that if we changed the modification time of the file, the module files
534 // info is still reusable if its content doesn't change.
535 CDB.addFile("N-part.cppm", R"cpp(
536export module N:Part;
537export int NPart = 43;
538 )cpp");
539 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
540
541 CDB.addFile("N.cppm", R"cpp(
542export module N;
543import :Part;
544import M;
545
546export int nn = 43;
547 )cpp");
548 // NInfo should be reusable after we change its content.
549 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
550}
551
552TEST_F(PrerequisiteModulesTests, CanReuseWithTransitiveNamedModuleImports) {
553 MockDirectoryCompilationDatabase CDB(TestDir, FS);
554
555 CDB.addFile("N.cppm", R"cpp(
556export module N;
557export inline constexpr int n = 1;
558 )cpp");
559
560 CDB.addFile("M.cppm", R"cpp(
561export module M;
562import N;
563export inline constexpr int m = n + 1;
564 )cpp");
565
566 CDB.addFile("A.cppm", R"cpp(
567export module A;
568import M;
569export inline constexpr int a = m + 1;
570 )cpp");
571
572 CDB.addFile("Use.cpp", R"cpp(
573import A;
574int use() { return a; }
575 )cpp");
576
577 ModulesBuilder Builder(CDB);
578
579 auto UseInfo =
580 Builder.buildPrerequisiteModulesFor(getFullPath("Use.cpp"), FS);
581 ASSERT_TRUE(UseInfo);
582
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"));
588
589 auto Invocation =
590 buildCompilerInvocation(getInputs("Use.cpp", CDB), DiagConsumer);
591 ASSERT_TRUE(Invocation);
592
593 EXPECT_TRUE(UseInfo->canReuse(*Invocation, FS.view(TestDir)));
594}
595
596// An End-to-End test for modules.
597TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
598 MockDirectoryCompilationDatabase CDB(TestDir, FS);
599
600 CDB.addFile("A.cppm", R"cpp(
601export module A;
602export void printA();
603 )cpp");
604
605 CDB.addFile("Use.cpp", R"cpp(
606import A;
607)cpp");
608
609 ModulesBuilder Builder(CDB);
610
611 ParseInputs Use = getInputs("Use.cpp", CDB);
612 Use.ModulesManager = &Builder;
613
614 std::unique_ptr<CompilerInvocation> CI =
615 buildCompilerInvocation(Use, DiagConsumer);
616 EXPECT_TRUE(CI);
617
618 auto Preamble =
619 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
620 /*Callback=*/nullptr);
621 EXPECT_TRUE(Preamble);
622 EXPECT_TRUE(Preamble->RequiredModules);
623
624 auto AST = ParsedAST::build(getFullPath("Use.cpp"), Use, std::move(CI), {},
625 Preamble);
626 EXPECT_TRUE(AST);
627
628 const NamedDecl &D = findDecl(*AST, "printA");
629 EXPECT_TRUE(D.isFromASTFile());
630}
631
632// An end to end test for code complete in modules
633TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
634 MockDirectoryCompilationDatabase CDB(TestDir, FS);
635
636 CDB.addFile("A.cppm", R"cpp(
637export module A;
638export void printA();
639 )cpp");
640
641 llvm::StringLiteral UserContents = R"cpp(
642import A;
643void func() {
644 print^
645}
646)cpp";
647
648 CDB.addFile("Use.cpp", UserContents);
649 Annotations Test(UserContents);
650
651 ModulesBuilder Builder(CDB);
652
653 ParseInputs Use = getInputs("Use.cpp", CDB);
654 Use.ModulesManager = &Builder;
655
656 std::unique_ptr<CompilerInvocation> CI =
657 buildCompilerInvocation(Use, DiagConsumer);
658 EXPECT_TRUE(CI);
659
660 auto Preamble =
661 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
662 /*Callback=*/nullptr);
663 EXPECT_TRUE(Preamble);
664 EXPECT_TRUE(Preamble->RequiredModules);
665
666 auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),
667 Preamble.get(), Use, {});
668 EXPECT_FALSE(Result.Completions.empty());
669 EXPECT_EQ(Result.Completions[0].Name, "printA");
670}
671
672TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
673 MockDirectoryCompilationDatabase CDB(TestDir, FS);
674
675 CDB.addFile("A.cppm", R"cpp(
676export module A;
677export void printA(int a);
678 )cpp");
679
680 llvm::StringLiteral UserContents = R"cpp(
681import A;
682void func() {
683 printA(^);
684}
685)cpp";
686
687 CDB.addFile("Use.cpp", UserContents);
688 Annotations Test(UserContents);
689
690 ModulesBuilder Builder(CDB);
691
692 ParseInputs Use = getInputs("Use.cpp", CDB);
693 Use.ModulesManager = &Builder;
694
695 std::unique_ptr<CompilerInvocation> CI =
696 buildCompilerInvocation(Use, DiagConsumer);
697 EXPECT_TRUE(CI);
698
699 auto Preamble =
700 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
701 /*Callback=*/nullptr);
702 EXPECT_TRUE(Preamble);
703 EXPECT_TRUE(Preamble->RequiredModules);
704
705 auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(), *Preamble,
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");
710}
711
712TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
713 MockDirectoryCompilationDatabase CDB(TestDir, FS);
714
715 CDB.addFile("M.cppm", R"cpp(
716export module M;
717export int M = 43;
718 )cpp");
719 CDB.addFile("A.cppm", R"cpp(
720export module A;
721import M;
722export int A = 43 + M;
723 )cpp");
724 CDB.addFile("B.cppm", R"cpp(
725export module B;
726import M;
727export int B = 44 + M;
728 )cpp");
729
730 ModulesBuilder Builder(CDB);
731
732 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
733 EXPECT_TRUE(AInfo);
734 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);
735 EXPECT_TRUE(BInfo);
736 HeaderSearchOptions HSOptsA(TestDir);
737 HeaderSearchOptions HSOptsB(TestDir);
738 AInfo->adjustHeaderSearchOptions(HSOptsA);
739 BInfo->adjustHeaderSearchOptions(HSOptsB);
740
741 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
742 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
743
744 // Check that we're reusing the module files.
745 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
746
747 // Update M.cppm to check if the modules builder can update correctly.
748 CDB.addFile("M.cppm", R"cpp(
749export module M;
750export constexpr int M = 43;
751 )cpp");
752
753 ParseInputs AUse = getInputs("A.cppm", CDB);
754 AUse.ModulesManager = &Builder;
755 std::unique_ptr<CompilerInvocation> AInvocation =
756 buildCompilerInvocation(AUse, DiagConsumer);
757 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));
758
759 ParseInputs BUse = getInputs("B.cppm", CDB);
760 AUse.ModulesManager = &Builder;
761 std::unique_ptr<CompilerInvocation> BInvocation =
762 buildCompilerInvocation(BUse, DiagConsumer);
763 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));
764
765 auto NewAInfo =
766 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
767 auto NewBInfo =
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);
775
776 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
777 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
778
779 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
780 // Persistent cache keeps the published BMI path stable, so verify the new
781 // module graph by reuse semantics instead of expecting a different path.
782 EXPECT_TRUE(NewAInfo->canReuse(*AInvocation, FS.view(TestDir)));
783 EXPECT_TRUE(NewBInfo->canReuse(*BInvocation, FS.view(TestDir)));
784}
785
786TEST_F(PrerequisiteModulesTests, ScanningCacheTest) {
787 MockDirectoryCompilationDatabase CDB(TestDir, FS);
788
789 CDB.addFile("M.cppm", R"cpp(
790export module M;
791 )cpp");
792 CDB.addFile("A.cppm", R"cpp(
793export module A;
794import M;
795 )cpp");
796 CDB.addFile("B.cppm", R"cpp(
797export module B;
798import M;
799 )cpp");
800
801 ModulesBuilder Builder(CDB);
802
803 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
804 Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);
805 EXPECT_EQ(CDB.getGlobalScanningCount(), 1u);
806}
807
808// Test that canReuse detects changes to headers included in module units.
809// This verifies that the ASTReader correctly tracks header file dependencies
810// in BMI files and that IsModuleFileUpToDate correctly validates them.
811TEST_F(PrerequisiteModulesTests, CanReuseWithHeadersInModuleUnit) {
812 MockDirectoryCompilationDatabase CDB(TestDir, FS);
813
814 // Create a header file that will be included in a module unit
815 CDB.addFile("header1.h", R"cpp(
816inline int getValue() { return 42; }
817 )cpp");
818
819 // Module M includes header1.h in the global module fragment
820 CDB.addFile("M.cppm", R"cpp(
821module;
822#include "header1.h"
823export module M;
824export int m_value = getValue();
825 )cpp");
826
827 // Module N imports M (similar structure to ReusabilityTest)
828 CDB.addFile("N.cppm", R"cpp(
829export module N;
830import :Part;
831import M;
832 )cpp");
833
834 // Add a module partition (similar to ReusabilityTest)
835 CDB.addFile("N-part.cppm", R"cpp(
836export module N:Part;
837 )cpp");
838
839 ModulesBuilder Builder(CDB);
840
841 // Build prerequisite modules for N (which depends on M)
842 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
843 ASSERT_TRUE(NInfo);
844
845 ParseInputs NInput = getInputs("N.cppm", CDB);
846 std::unique_ptr<CompilerInvocation> Invocation =
847 buildCompilerInvocation(NInput, DiagConsumer);
848
849 // Initially, canReuse should return true
850 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
851
852 // Test 1: Modify header1.h (included by M)
853 // canReuse should detect this change since M's BMI records header1.h as input
854 CDB.addFile("header1.h", R"cpp(
855inline int getValue() { return 43; }
856 )cpp");
857 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
858
859 // Rebuild and verify canReuse returns true again
860 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
861 ASSERT_TRUE(NInfo);
862 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
863
864 // Test 2: Modify the module source file itself
865 CDB.addFile("M.cppm", R"cpp(
866module;
867#include "header1.h"
868export module M;
869export int m_value = getValue();
870export int m_new_value = 10;
871 )cpp");
872 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
873
874 // Rebuild after module source change
875 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
876 ASSERT_TRUE(NInfo);
877 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
878}
879
880TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileTest) {
881 MockDirectoryCompilationDatabase CDB(TestDir, FS);
882
883 CDB.addFile("M.cppm", R"cpp(
884export module M;
885 )cpp");
886
887 CDB.addFile("U.cpp", R"cpp(
888import M;
889 )cpp");
890
891 // Use ModulesBuilder to produce the prebuilt module file.
892 ModulesBuilder Builder(CDB);
893 auto ModuleInfo =
894 Builder.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS);
895 HeaderSearchOptions HS(TestDir);
896 ModuleInfo->adjustHeaderSearchOptions(HS);
897
898 CDB.ExtraClangFlags.push_back("-fmodule-file=M=" +
899 HS.PrebuiltModuleFiles["M"]);
900 ModulesBuilder Builder2(CDB);
901 auto ModuleInfo2 =
902 Builder2.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS);
903 HeaderSearchOptions HS2(TestDir);
904 ModuleInfo2->adjustHeaderSearchOptions(HS2);
905
906 EXPECT_EQ(HS.PrebuiltModuleFiles, HS2.PrebuiltModuleFiles);
907}
908
909// Test that prebuilt module files with relative paths are correctly resolved.
910// This tests the fix for the issue where clangd couldn't find BMI files when
911// the compilation database contained relative paths in -fmodule-file=
912// arguments.
913TEST_F(PrerequisiteModulesTests, PrebuiltModuleFileWithRelativePath) {
914 MockDirectoryCompilationDatabase CDB(TestDir, FS);
915
916 CDB.addFile("M.cppm", R"cpp(
917export module M;
918export int m_value = 42;
919 )cpp");
920
921 CDB.addFile("U.cpp", R"cpp(
922import M;
923int use() { return m_value; }
924 )cpp");
925
926 // Step 1: Build the module file using ModulesBuilder
927 ModulesBuilder Builder(CDB);
928 auto ModuleInfo =
929 Builder.buildPrerequisiteModulesFor(getFullPath("U.cpp"), FS);
930 ASSERT_TRUE(ModuleInfo);
931
932 HeaderSearchOptions HS(TestDir);
933 ModuleInfo->adjustHeaderSearchOptions(HS);
934 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
935
936 // Get the absolute path of the built module file
937 std::string OriginalBMPath = HS.PrebuiltModuleFiles["M"];
938 ASSERT_TRUE(llvm::sys::path::is_absolute(OriginalBMPath));
939 ASSERT_TRUE(llvm::sys::fs::exists(OriginalBMPath));
940
941 // Step 2: Create a subdirectory in TestDir and copy the BMI there
942 SmallString<256> BMSubDir(TestDir);
943 llvm::sys::path::append(BMSubDir, "prebuilt_modules");
944 ASSERT_FALSE(llvm::sys::fs::create_directories(BMSubDir));
945
946 SmallString<256> NewBMPath(BMSubDir);
947 llvm::sys::path::append(NewBMPath, "M.pcm");
948
949 // Copy the BMI file to the new location
950 ASSERT_FALSE(llvm::sys::fs::copy_file(OriginalBMPath, NewBMPath));
951 ASSERT_TRUE(llvm::sys::fs::exists(NewBMPath));
952
953 // Step 3: Create a relative path from the new absolute path
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));
958
959 // Step 4: Create a new CDB with relative path in -fmodule-file=
960 MockDirectoryCompilationDatabase CDBWithRelativePath(TestDir, FS);
961
962 CDBWithRelativePath.addFile("M.cppm", R"cpp(
963export module M;
964export int m_value = 42;
965 )cpp");
966
967 CDBWithRelativePath.addFile("U.cpp", R"cpp(
968import M;
969int use() { return m_value; }
970 )cpp");
971
972 // Use relative path in -fmodule-file= argument
973 CDBWithRelativePath.ExtraClangFlags.push_back("-fmodule-file=M=" +
974 RelativeBMPath);
975
976 // Step 5: Verify that clangd can find and reuse the prebuilt module file
977 ModulesBuilder BuilderWithRelativePath(CDBWithRelativePath);
978 auto ModuleInfo2 = BuilderWithRelativePath.buildPrerequisiteModulesFor(
979 getFullPath("U.cpp"), FS);
980 ASSERT_TRUE(ModuleInfo2);
981
982 HeaderSearchOptions HS2(TestDir);
983 ModuleInfo2->adjustHeaderSearchOptions(HS2);
984
985 // The module file should be found and the paths should match
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;
991}
992
993TEST_F(PrerequisiteModulesTests,
994 UniqueModuleNameStateResolvedFromCompileCommands) {
995 PerFileModulesCompilationDatabase CDB(TestDir, FS);
996
997 SmallString<256> MPcm(TestDir);
998 llvm::sys::path::append(MPcm, "build", "M.pcm");
999
1000 CDB.addFile("M.cppm", R"cpp(
1001export module M;
1002export int value = 1;
1003 )cpp",
1004 {"--precompile", "-o", std::string(MPcm)});
1005 CDB.addFile("A.cpp", R"cpp(
1006import M;
1007int useA() { return value; }
1008 )cpp",
1009 {"-fmodule-file=M=" + std::string(MPcm)});
1010 CDB.addFile("B.cpp", R"cpp(
1011import M;
1012int useB() { return value; }
1013 )cpp",
1014 {"-fmodule-file=M=" + std::string(MPcm)});
1015
1016 auto ProjectModules = CDB.getProjectModules(getFullPath("A.cpp"));
1017 ASSERT_TRUE(ProjectModules);
1018
1019 EXPECT_EQ(ProjectModules->getModuleNameState("M"),
1021}
1022
1023TEST_F(PrerequisiteModulesTests,
1024 DuplicateModuleNamesResolvedFromCompileCommands) {
1025 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1026
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");
1031
1032 CDB.addFile("a/M.cppm", R"cpp(
1033export module M;
1034export int onlyA = 1;
1035 )cpp",
1036 {"--precompile", "-o", std::string(APcm)});
1037 CDB.addFile("b/M.cppm", R"cpp(
1038export module M;
1039export int onlyB = 2;
1040 )cpp",
1041 {"--precompile", "-o", std::string(BPcm)});
1042 CDB.addFile("a/Use.cpp", R"cpp(
1043import M;
1044int useA() { return onlyA; }
1045 )cpp",
1046 {"-fmodule-file=M=" + std::string(APcm)});
1047 CDB.addFile("b/Use.cpp", R"cpp(
1048import M;
1049int useB() { return onlyB; }
1050 )cpp",
1051 {"-fmodule-file=M=" + std::string(BPcm)});
1052
1053 auto ProjectModules = CDB.getProjectModules(getFullPath("a/Use.cpp"));
1054 ASSERT_TRUE(ProjectModules);
1055 EXPECT_EQ(ProjectModules->getModuleNameState("M"),
1057
1058 EXPECT_EQ(
1059 ProjectModules->getSourceForModuleName("M", getFullPath("a/Use.cpp")),
1060 getFullPath("a/M.cppm"));
1061 EXPECT_EQ(
1062 ProjectModules->getSourceForModuleName("M", getFullPath("b/Use.cpp")),
1063 getFullPath("b/M.cppm"));
1064}
1065
1066TEST_F(PrerequisiteModulesTests,
1067 DuplicateModuleNamesResolvedFromResponseFiles) {
1068 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1069
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");
1074
1075 SmallString<256> RspDir(TestDir);
1076 llvm::sys::path::append(RspDir, "build", "rsp");
1077 ASSERT_FALSE(llvm::sys::fs::create_directories(RspDir));
1078
1079 SmallString<256> AMRsp(RspDir);
1080 llvm::sys::path::append(AMRsp, "a-m.rsp");
1081 {
1082 std::error_code EC;
1083 llvm::raw_fd_ostream OS(AMRsp, EC);
1084 ASSERT_FALSE(EC);
1085 OS << "-x c++-module -fmodule-output=" << APcm;
1086 OS.close();
1087 }
1088
1089 SmallString<256> BMRsp(RspDir);
1090 llvm::sys::path::append(BMRsp, "b-m.rsp");
1091 {
1092 std::error_code EC;
1093 llvm::raw_fd_ostream OS(BMRsp, EC);
1094 ASSERT_FALSE(EC);
1095 OS << "-x c++-module -fmodule-output=" << BPcm;
1096 OS.close();
1097 }
1098
1099 SmallString<256> AUseRsp(RspDir);
1100 llvm::sys::path::append(AUseRsp, "a-use.rsp");
1101 {
1102 std::error_code EC;
1103 llvm::raw_fd_ostream OS(AUseRsp, EC);
1104 ASSERT_FALSE(EC);
1105 OS << "-fmodule-file=M=" << APcm;
1106 OS.close();
1107 }
1108
1109 SmallString<256> BUseRsp(RspDir);
1110 llvm::sys::path::append(BUseRsp, "b-use.rsp");
1111 {
1112 std::error_code EC;
1113 llvm::raw_fd_ostream OS(BUseRsp, EC);
1114 ASSERT_FALSE(EC);
1115 OS << "-fmodule-file=M=" << BPcm;
1116 OS.close();
1117 }
1118
1119 CDB.addFile("a/M.cppm", R"cpp(
1120export module M;
1121export int onlyA = 1;
1122 )cpp",
1123 {"@" + std::string(AMRsp)});
1124 CDB.addFile("b/M.cppm", R"cpp(
1125export module M;
1126export int onlyB = 2;
1127 )cpp",
1128 {"@" + std::string(BMRsp)});
1129 CDB.addFile("a/Use.cpp", R"cpp(
1130import M;
1131int useA() { return onlyA; }
1132 )cpp",
1133 {"@" + std::string(AUseRsp)});
1134 CDB.addFile("b/Use.cpp", R"cpp(
1135import M;
1136int useB() { return onlyB; }
1137 )cpp",
1138 {"@" + std::string(BUseRsp)});
1139
1140 auto ProjectModules = CDB.getProjectModules(getFullPath("a/Use.cpp"));
1141 ASSERT_TRUE(ProjectModules);
1142 EXPECT_EQ(ProjectModules->getModuleNameState("M"),
1144
1145 EXPECT_EQ(
1146 ProjectModules->getSourceForModuleName("M", getFullPath("a/Use.cpp")),
1147 getFullPath("a/M.cppm"));
1148 EXPECT_EQ(
1149 ProjectModules->getSourceForModuleName("M", getFullPath("b/Use.cpp")),
1150 getFullPath("b/M.cppm"));
1151}
1152
1153TEST_F(PrerequisiteModulesTests, DuplicateModuleNamesKeepSeparateBMICache) {
1154 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1155
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");
1160
1161 CDB.addFile("a/M.cppm", R"cpp(
1162export module M;
1163export int onlyA = 1;
1164 )cpp",
1165 {"--precompile", "-o", std::string(APcm)});
1166 CDB.addFile("b/M.cppm", R"cpp(
1167export module M;
1168export int onlyB = 2;
1169 )cpp",
1170 {"--precompile", "-o", std::string(BPcm)});
1171 CDB.addFile("a/Use.cpp", R"cpp(
1172import M;
1173int useA() { return onlyA; }
1174 )cpp",
1175 {"-fmodule-file=M=" + std::string(APcm)});
1176 CDB.addFile("b/Use.cpp", R"cpp(
1177import M;
1178int useB() { return onlyB; }
1179 )cpp",
1180 {"-fmodule-file=M=" + std::string(BPcm)});
1181
1182 ModulesBuilder Builder(CDB);
1183
1184 auto AInfo =
1185 Builder.buildPrerequisiteModulesFor(getFullPath("a/Use.cpp"), FS);
1186 auto BInfo =
1187 Builder.buildPrerequisiteModulesFor(getFullPath("b/Use.cpp"), FS);
1188 ASSERT_TRUE(AInfo);
1189 ASSERT_TRUE(BInfo);
1190
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"]);
1198
1199 auto UseA = getInputs("a/Use.cpp", CDB);
1200 UseA.ModulesManager = &Builder;
1201 auto CIA = buildCompilerInvocation(UseA, DiagConsumer);
1202 ASSERT_TRUE(CIA);
1203 auto PreambleA = buildPreamble(getFullPath("a/Use.cpp"), *CIA, UseA,
1204 /*InMemory=*/true, /*Callback=*/nullptr);
1205 ASSERT_TRUE(PreambleA);
1206 auto ASTA = ParsedAST::build(getFullPath("a/Use.cpp"), UseA, std::move(CIA),
1207 {}, PreambleA);
1208 ASSERT_TRUE(ASTA);
1209 EXPECT_TRUE(findDecl(*ASTA, "onlyA").isFromASTFile());
1210
1211 auto UseB = getInputs("b/Use.cpp", CDB);
1212 UseB.ModulesManager = &Builder;
1213 auto CIB = buildCompilerInvocation(UseB, DiagConsumer);
1214 ASSERT_TRUE(CIB);
1215 auto PreambleB = buildPreamble(getFullPath("b/Use.cpp"), *CIB, UseB,
1216 /*InMemory=*/true, /*Callback=*/nullptr);
1217 ASSERT_TRUE(PreambleB);
1218 auto ASTB = ParsedAST::build(getFullPath("b/Use.cpp"), UseB, std::move(CIB),
1219 {}, PreambleB);
1220 ASSERT_TRUE(ASTB);
1221 EXPECT_TRUE(findDecl(*ASTB, "onlyB").isFromASTFile());
1222}
1223
1224TEST_F(PrerequisiteModulesTests, PersistentModuleCacheReusedAcrossBuilders) {
1225 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1226
1227 CDB.addFile("M.cppm", R"cpp(
1228export module M;
1229export int MValue = 43;
1230 )cpp");
1231 CDB.addFile("A.cppm", R"cpp(
1232export module A;
1233import M;
1234export int AValue = MValue;
1235 )cpp");
1236
1237 std::string FirstPCMPath;
1238 {
1239 ModulesBuilder Builder(CDB);
1240 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1241 ASSERT_TRUE(AInfo);
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"));
1248 }
1249
1250 EXPECT_FALSE(llvm::sys::fs::exists(FirstPCMPath));
1251
1252 // A fresh builder should reuse the persistent BMI published by the first one
1253 // instead of rebuilding its stable cache entry.
1254 ModulesBuilder Builder(CDB);
1255 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1256 ASSERT_TRUE(AInfo);
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-"));
1262
1263 ParseInputs AUse = getInputs("A.cppm", CDB);
1264 AUse.ModulesManager = &Builder;
1265 auto Invocation = buildCompilerInvocation(AUse, DiagConsumer);
1266 ASSERT_TRUE(Invocation);
1267 EXPECT_TRUE(AInfo->canReuse(*Invocation, FS.view(TestDir)));
1268}
1269
1270TEST_F(PrerequisiteModulesTests,
1271 PersistentModuleCacheRebuildsAfterDeletingStalePCM) {
1272 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1273
1274 CDB.addFile("M.cppm", R"cpp(
1275export module M;
1276export int MValue = 43;
1277 )cpp");
1278 CDB.addFile("A.cppm", R"cpp(
1279export module A;
1280import M;
1281export int AValue = MValue;
1282 )cpp");
1283
1284 ModulesBuilder Builder(CDB);
1285 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1286 ASSERT_TRUE(AInfo);
1287 HeaderSearchOptions HS(TestDir);
1288 AInfo->adjustHeaderSearchOptions(HS);
1289 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
1290 std::string PCMPath = HS.PrebuiltModuleFiles["M"];
1291
1292 std::error_code EC;
1293 llvm::raw_fd_ostream OS(PCMPath, EC);
1294 ASSERT_FALSE(EC);
1295 OS << "broken";
1296 OS.close();
1297
1298 // Corrupt the handed-out BMI and ensure clangd rebuilds a usable replacement.
1299 auto NewAInfo =
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-"));
1307
1308 ParseInputs AUse = getInputs("A.cppm", CDB);
1309 AUse.ModulesManager = &Builder;
1310 auto Invocation = buildCompilerInvocation(AUse, DiagConsumer);
1311 ASSERT_TRUE(Invocation);
1312 EXPECT_TRUE(NewAInfo->canReuse(*Invocation, FS.view(TestDir)));
1313}
1314
1315TEST_F(PrerequisiteModulesTests, PersistentModuleCacheCreatesSourceHashLock) {
1316 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1317
1318 CDB.addFile("M.cppm", R"cpp(
1319export module M;
1320export int MValue = 43;
1321 )cpp");
1322 CDB.addFile("A.cppm", R"cpp(
1323export module A;
1324import M;
1325export int AValue = MValue;
1326 )cpp");
1327
1328 ModulesBuilder Builder(CDB);
1329 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1330 ASSERT_TRUE(AInfo);
1331
1332 HeaderSearchOptions HS(TestDir);
1333 AInfo->adjustHeaderSearchOptions(HS);
1334 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
1335
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);
1342
1343 // Locks live next to the persistent cache and are keyed by source-hash so
1344 // builders publishing the same module unit serialize with each other.
1345 llvm::StringRef SourceDirectoryName =
1346 llvm::sys::path::filename(SourceHashDir);
1347 // Split from the right because the readable basename may also contain '-'.
1348 llvm::StringRef SourceHash = SourceDirectoryName.rsplit('-').second;
1349 llvm::SmallString<256> LockPath(CacheRoot);
1350 llvm::sys::path::append(LockPath, ".locks", SourceHash);
1351
1352 EXPECT_TRUE(llvm::sys::fs::exists(LockPath));
1353}
1354
1355TEST_F(PrerequisiteModulesTests,
1356 PersistentModuleCacheGCRemovesOldStablePublishedModule) {
1357 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1358
1359 CDB.addFile("M.cppm", R"cpp(
1360export module M;
1361export int MValue = 43;
1362 )cpp");
1363 CDB.addFile("A.cppm", R"cpp(
1364export module A;
1365import M;
1366export int AValue = MValue;
1367 )cpp");
1368
1369 llvm::SmallString<256> OrphanPCMPath;
1370 {
1371 ModulesBuilder Builder(CDB);
1372 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1373 ASSERT_TRUE(AInfo);
1374 HeaderSearchOptions HS(TestDir);
1375 AInfo->adjustHeaderSearchOptions(HS);
1376 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
1377
1378 OrphanPCMPath = HS.PrebuiltModuleFiles["M"];
1379 llvm::sys::path::remove_filename(OrphanPCMPath);
1380 llvm::sys::path::append(OrphanPCMPath, "Orphan.pcm");
1381
1382 std::error_code EC;
1383 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1384 ASSERT_FALSE(EC);
1385 OS << "orphan";
1386 OS.close();
1387 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1388
1389 int FD = -1;
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));
1398 }
1399
1400 ModulesBuilder Builder(CDB);
1401 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1402 ASSERT_TRUE(AInfo);
1403 EXPECT_FALSE(llvm::sys::fs::exists(OrphanPCMPath));
1404}
1405
1406TEST_F(PrerequisiteModulesTests,
1407 PersistentModuleCacheGCKeepsRecentStablePublishedModule) {
1408 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1409
1410 CDB.addFile("M.cppm", R"cpp(
1411export module M;
1412export int MValue = 43;
1413 )cpp");
1414 CDB.addFile("A.cppm", R"cpp(
1415export module A;
1416import M;
1417export int AValue = MValue;
1418 )cpp");
1419
1420 llvm::SmallString<256> OrphanPCMPath;
1421 {
1422 ModulesBuilder Builder(CDB);
1423 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1424 ASSERT_TRUE(AInfo);
1425 HeaderSearchOptions HS(TestDir);
1426 AInfo->adjustHeaderSearchOptions(HS);
1427 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
1428
1429 OrphanPCMPath = HS.PrebuiltModuleFiles["M"];
1430 llvm::sys::path::remove_filename(OrphanPCMPath);
1431 llvm::sys::path::append(OrphanPCMPath, "Orphan.pcm");
1432
1433 std::error_code EC;
1434 llvm::raw_fd_ostream OS(OrphanPCMPath, EC);
1435 ASSERT_FALSE(EC);
1436 OS << "orphan";
1437 OS.close();
1438 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1439 }
1440
1441 ModulesBuilder Builder(CDB);
1442 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1443 ASSERT_TRUE(AInfo);
1444 EXPECT_TRUE(llvm::sys::fs::exists(OrphanPCMPath));
1445}
1446
1447TEST_F(PrerequisiteModulesTests,
1448 PersistentModuleCacheGCRemovesOldVersionedModuleFile) {
1449 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1450
1451 CDB.addFile("M.cppm", R"cpp(
1452export module M;
1453export int MValue = 43;
1454 )cpp");
1455 CDB.addFile("A.cppm", R"cpp(
1456export module A;
1457import M;
1458export int AValue = MValue;
1459 )cpp");
1460
1461 llvm::SmallString<256> OldVersionedPCMPath;
1462 {
1463 ModulesBuilder Builder(CDB);
1464 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1465 ASSERT_TRUE(AInfo);
1466 HeaderSearchOptions HS(TestDir);
1467 AInfo->adjustHeaderSearchOptions(HS);
1468 ASSERT_EQ(HS.PrebuiltModuleFiles.count("M"), 1u);
1469
1470 OldVersionedPCMPath = HS.PrebuiltModuleFiles["M"];
1471 ASSERT_TRUE(llvm::sys::fs::exists(OldVersionedPCMPath));
1472
1473 int FD = -1;
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));
1482 }
1483
1484 ModulesBuilder Builder(CDB);
1485 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1486 ASSERT_TRUE(AInfo);
1487 EXPECT_FALSE(llvm::sys::fs::exists(OldVersionedPCMPath));
1488}
1489
1490TEST_F(PrerequisiteModulesTests,
1491 PersistentModuleCacheGCKeepsRecentVersionedModuleFile) {
1492 PerFileModulesCompilationDatabase CDB(TestDir, FS);
1493
1494 CDB.addFile("M.cppm", R"cpp(
1495export module M;
1496export int MValue = 43;
1497 )cpp");
1498 CDB.addFile("A.cppm", R"cpp(
1499export module A;
1500import M;
1501export int AValue = MValue;
1502 )cpp");
1503
1504 auto FirstBuilder = std::make_unique<ModulesBuilder>(CDB);
1505 auto AInfo =
1506 FirstBuilder->buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1507 ASSERT_TRUE(AInfo);
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));
1513
1514 ModulesBuilder SecondBuilder(CDB);
1515 auto SecondInfo =
1516 SecondBuilder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
1517 ASSERT_TRUE(SecondInfo);
1518 EXPECT_TRUE(llvm::sys::fs::exists(CopyOnReadPCMPath));
1519}
1520
1521TEST_F(PrerequisiteModulesTests,
1522 PersistentModuleCacheIgnoresRequiredSourceForOnDiskPath) {
1523 ModuleUnitRootCompilationDatabase CDB(TestDir, FS);
1524
1525 CDB.addFile("shared/M.cppm", R"cpp(
1526export module M;
1527export int MValue = 43;
1528 )cpp");
1529 CDB.addFile("projA/A.cppm", R"cpp(
1530export module A;
1531import M;
1532export int AValue = MValue;
1533 )cpp");
1534 CDB.addFile("projB/B.cppm", R"cpp(
1535export module B;
1536import M;
1537export int BValue = MValue;
1538 )cpp");
1539
1540 ModulesBuilder Builder(CDB);
1541
1542 auto AInfo =
1543 Builder.buildPrerequisiteModulesFor(getFullPath("projA/A.cppm"), FS);
1544 auto BInfo =
1545 Builder.buildPrerequisiteModulesFor(getFullPath("projB/B.cppm"), FS);
1546 ASSERT_TRUE(AInfo);
1547 ASSERT_TRUE(BInfo);
1548
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"]));
1557
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");
1561 EXPECT_TRUE(
1562 StringRef(HSA.PrebuiltModuleFiles["M"]).starts_with(ExpectedRoot));
1563 EXPECT_TRUE(StringRef(HSA.PrebuiltModuleFiles["M"]).contains("M.cppm-"));
1564 EXPECT_TRUE(
1565 StringRef(HSB.PrebuiltModuleFiles["M"]).starts_with(ExpectedRoot));
1566 EXPECT_TRUE(StringRef(HSB.PrebuiltModuleFiles["M"]).contains("M.cppm-"));
1567}
1568
1569TEST_F(PrerequisiteModulesTests, ModuleImportThroughInclude) {
1570 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1571
1572 Annotations UseCpp(R"cpp(
1573#include "Header.hpp"
1574void use() {
1575 TypeFrom^Module t1;
1576 TypeFromHeader t2;
1577}
1578)cpp");
1579
1580 CDB.addFile("M.cppm", R"cpp(
1581export module M;
1582export struct TypeFromModule {};
1583)cpp");
1584
1585 CDB.addFile("Header.hpp", R"cpp(
1586import M;
1587struct TypeFromHeader {};
1588)cpp");
1589
1590 CDB.addFile("Use.cpp", UseCpp.code());
1591
1592 ModulesBuilder Builder(CDB);
1593
1594 auto Inputs = getInputs("Use.cpp", CDB);
1595 Inputs.ModulesManager = &Builder;
1596 Inputs.Opts.SkipPreambleBuild = true;
1597
1598 auto CI = buildCompilerInvocation(Inputs, DiagConsumer);
1599 ASSERT_TRUE(CI);
1600
1601 auto Preamble =
1602 buildPreamble(getFullPath("Use.cpp"), *CI, Inputs, /*StoreInMemory=*/true,
1603 /*PeambleCallback=*/nullptr);
1604 ASSERT_TRUE(Preamble);
1605 EXPECT_EQ(Preamble->Preamble.getBounds().Size, 0u);
1606
1607 auto AST = ParsedAST::build(getFullPath("Use.cpp"), Inputs, std::move(CI), {},
1608 Preamble);
1609 ASSERT_TRUE(AST);
1610 EXPECT_TRUE(AST->getDiagnostics().empty());
1611
1612 auto Result = codeComplete(getFullPath("Use.cpp"), UseCpp.point(),
1613 Preamble.get(), Inputs, {});
1614 EXPECT_THAT(Result.Completions,
1615 testing::UnorderedElementsAre(named("TypeFromModule"),
1616 named("TypeFromHeader")));
1617}
1618
1619TEST_F(PrerequisiteModulesTests,
1620 SkipPreambleBuildInvalidatedByNewModuleImport) {
1621 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1622
1623 CDB.addFile("Dep.cppm", R"cpp(
1624export module Dep;
1625)cpp");
1626
1627 CDB.addFile("Consumer.cpp", R"cpp(
1628import Dep;
1629void use() {}
1630)cpp");
1631
1632 ModulesBuilder Builder(CDB);
1633
1634 auto Inputs = getInputs("Consumer.cpp", CDB);
1635 Inputs.ModulesManager = &Builder;
1636 Inputs.Opts.SkipPreambleBuild = true;
1637
1638 auto CI = buildCompilerInvocation(Inputs, DiagConsumer);
1639 ASSERT_TRUE(CI);
1640
1641 auto Preamble = buildPreamble(getFullPath("Consumer.cpp"), *CI, Inputs,
1642 /*StoreInMemory=*/true,
1643 /*PreambleCallback=*/nullptr);
1644 ASSERT_TRUE(Preamble);
1645 EXPECT_EQ(Preamble->Preamble.getBounds().Size, 0u);
1646 ASSERT_TRUE(Preamble->RequiredModules);
1647
1648 CDB.addFile("NewDep.cppm", R"cpp(
1649export module NewDep;
1650)cpp");
1651
1652 // Add a new import.
1653 Inputs.Contents = R"cpp(
1654import Dep;
1655import NewDep;
1656void use() {}
1657)cpp";
1658
1659 {
1660 std::error_code EC;
1661 llvm::raw_fd_ostream OS(getFullPath("Consumer.cpp"), EC,
1662 llvm::sys::fs::OF_None);
1663 ASSERT_FALSE(EC);
1664 OS << Inputs.Contents;
1665 }
1666
1667 auto NewCI = buildCompilerInvocation(Inputs, DiagConsumer);
1668 ASSERT_TRUE(NewCI);
1669
1670 EXPECT_FALSE(isPreambleCompatible(*Preamble, Inputs,
1671 getFullPath("Consumer.cpp"), *NewCI));
1672}
1673
1674TEST_F(PrerequisiteModulesTests, ModuleSemanticHighlighting) {
1675 MockDirectoryCompilationDatabase CDB(TestDir, FS);
1676
1677 llvm::StringRef AnnotatedCode = R"cpp(
1678 module;
1679 $import[[import]] M;
1680 export module highlight;
1681 $export[[export]] void foo() {
1682 }
1683)cpp";
1684 Annotations UseCpp(AnnotatedCode);
1685
1686 CDB.addFile("M.cppm", R"cpp(
1687export module M;
1688export struct TypeFromModule {};
1689)cpp");
1690
1691 CDB.addFile("Use.cpp", UseCpp.code());
1692
1693 ModulesBuilder Builder(CDB);
1694
1695 auto Inputs = getInputs("Use.cpp", CDB);
1696 Inputs.ModulesManager = &Builder;
1697 Inputs.Opts.SkipPreambleBuild = true;
1698
1699 auto CI = buildCompilerInvocation(Inputs, DiagConsumer);
1700 ASSERT_TRUE(CI);
1701
1702 auto Preamble =
1703 buildPreamble(getFullPath("Use.cpp"), *CI, Inputs, /*StoreInMemory=*/true,
1704 /*PeambleCallback=*/nullptr);
1705 ASSERT_TRUE(Preamble);
1706
1707 auto AST = ParsedAST::build(getFullPath("Use.cpp"), Inputs, std::move(CI), {},
1708 Preamble);
1709
1710 ASSERT_TRUE(AST);
1711
1712 auto Actual = getSemanticHighlightings(AST.value(),
1713 /*IncludeInactiveRegionTokens=*/true);
1714 auto HasToken = [&](llvm::StringRef Name, HighlightingKind Kind) {
1715 return llvm::any_of(Actual, [&](const HighlightingToken &T) {
1716 return T.Kind == Kind && T.R == UseCpp.range(Name);
1717 });
1718 };
1719 EXPECT_TRUE(HasToken("import", HighlightingKind::Modifier));
1720 EXPECT_TRUE(HasToken("export", HighlightingKind::Modifier));
1721}
1722
1723} // namespace
1724} // namespace clang::clangd
1725
1726#endif
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
std::string CommandLine
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.
Definition AST.cpp:44
std::vector< HighlightingToken > getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens)
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
std::string maybeCaseFoldPath(PathRef Path)
Definition Path.cpp:18
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.
Definition Compiler.cpp:96
MATCHER_P(named, N, "")
bool isPreambleCompatible(const PreambleData &Preamble, const ParseInputs &Inputs, PathRef FileName, const CompilerInvocation &CI)
Returns true if Preamble is reusable for Inputs.
Definition Preamble.cpp:723
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.
Definition Preamble.cpp:573
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
std::string Path
A typedef to represent a file path.
Definition Path.h:26
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.