[LLD][COFF] Emit locally imported EC symbols for ARM64X (#125527)
[llvm-project.git] / clang-tools-extra / clangd / unittests / PrerequisiteModulesTest.cpp
blob51723c797eabcf5b364bcf02ceb1b9fe8d6e2c6a
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 //===----------------------------------------------------------------------===//
10 /// FIXME: Skip testing on windows temporarily due to the different escaping
11 /// code mode.
12 #ifndef _WIN32
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"
27 namespace clang::clangd {
28 namespace {
30 class MockDirectoryCompilationDatabase : public MockCompilationDatabase {
31 public:
32 MockDirectoryCompilationDatabase(StringRef TestDir, const ThreadsafeFS &TFS)
33 : MockCompilationDatabase(TestDir),
34 MockedCDBPtr(std::make_shared<MockClangCompilationDatabase>(*this)),
35 TFS(TFS) {
36 this->ExtraClangFlags.push_back("-std=c++20");
37 this->ExtraClangFlags.push_back("-c");
40 void addFile(llvm::StringRef Path, llvm::StringRef Contents);
42 std::unique_ptr<ProjectModules> getProjectModules(PathRef) const override {
43 return scanningProjectModules(MockedCDBPtr, TFS);
46 private:
47 class MockClangCompilationDatabase : public tooling::CompilationDatabase {
48 public:
49 MockClangCompilationDatabase(MockDirectoryCompilationDatabase &MCDB)
50 : MCDB(MCDB) {}
52 std::vector<tooling::CompileCommand>
53 getCompileCommands(StringRef FilePath) const override {
54 std::optional<tooling::CompileCommand> Cmd =
55 MCDB.getCompileCommand(FilePath);
56 EXPECT_TRUE(Cmd);
57 return {*Cmd};
60 std::vector<std::string> getAllFiles() const override { return Files; }
62 void AddFile(StringRef File) { Files.push_back(File.str()); }
64 private:
65 MockDirectoryCompilationDatabase &MCDB;
66 std::vector<std::string> Files;
69 std::shared_ptr<MockClangCompilationDatabase> MockedCDBPtr;
70 const ThreadsafeFS &TFS;
73 // Add files to the working testing directory and the compilation database.
74 void MockDirectoryCompilationDatabase::addFile(llvm::StringRef Path,
75 llvm::StringRef Contents) {
76 ASSERT_FALSE(llvm::sys::path::is_absolute(Path));
78 SmallString<256> AbsPath(Directory);
79 llvm::sys::path::append(AbsPath, Path);
81 ASSERT_FALSE(
82 llvm::sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
84 std::error_code EC;
85 llvm::raw_fd_ostream OS(AbsPath, EC);
86 ASSERT_FALSE(EC);
87 OS << Contents;
89 MockedCDBPtr->AddFile(Path);
92 class PrerequisiteModulesTests : public ::testing::Test {
93 protected:
94 void SetUp() override {
95 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("modules-test", TestDir));
98 void TearDown() override {
99 ASSERT_FALSE(llvm::sys::fs::remove_directories(TestDir));
102 public:
103 // Get the absolute path for file specified by Path under testing working
104 // directory.
105 std::string getFullPath(llvm::StringRef Path) {
106 SmallString<128> Result(TestDir);
107 llvm::sys::path::append(Result, Path);
108 EXPECT_TRUE(llvm::sys::fs::exists(Result.str()));
109 return Result.str().str();
112 ParseInputs getInputs(llvm::StringRef FileName,
113 const GlobalCompilationDatabase &CDB) {
114 std::string FullPathName = getFullPath(FileName);
116 ParseInputs Inputs;
117 std::optional<tooling::CompileCommand> Cmd =
118 CDB.getCompileCommand(FullPathName);
119 EXPECT_TRUE(Cmd);
120 Inputs.CompileCommand = std::move(*Cmd);
121 Inputs.TFS = &FS;
123 if (auto Contents = FS.view(TestDir)->getBufferForFile(FullPathName))
124 Inputs.Contents = Contents->get()->getBuffer().str();
126 return Inputs;
129 SmallString<256> TestDir;
130 // FIXME: It will be better to use the MockFS if the scanning process and
131 // build module process doesn't depend on reading real IO.
132 RealThreadsafeFS FS;
134 DiagnosticConsumer DiagConsumer;
137 TEST_F(PrerequisiteModulesTests, NonModularTest) {
138 MockDirectoryCompilationDatabase CDB(TestDir, FS);
140 CDB.addFile("foo.h", R"cpp(
141 inline void foo() {}
142 )cpp");
144 CDB.addFile("NonModular.cpp", R"cpp(
145 #include "foo.h"
146 void use() {
147 foo();
149 )cpp");
151 ModulesBuilder Builder(CDB);
153 // NonModular.cpp is not related to modules. So nothing should be built.
154 auto NonModularInfo =
155 Builder.buildPrerequisiteModulesFor(getFullPath("NonModular.cpp"), FS);
156 EXPECT_TRUE(NonModularInfo);
158 HeaderSearchOptions HSOpts;
159 NonModularInfo->adjustHeaderSearchOptions(HSOpts);
160 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
162 auto Invocation =
163 buildCompilerInvocation(getInputs("NonModular.cpp", CDB), DiagConsumer);
164 EXPECT_TRUE(NonModularInfo->canReuse(*Invocation, FS.view(TestDir)));
167 TEST_F(PrerequisiteModulesTests, ModuleWithoutDepTest) {
168 MockDirectoryCompilationDatabase CDB(TestDir, FS);
170 CDB.addFile("foo.h", R"cpp(
171 inline void foo() {}
172 )cpp");
174 CDB.addFile("M.cppm", R"cpp(
175 module;
176 #include "foo.h"
177 export module M;
178 )cpp");
180 ModulesBuilder Builder(CDB);
182 auto MInfo = Builder.buildPrerequisiteModulesFor(getFullPath("M.cppm"), FS);
183 EXPECT_TRUE(MInfo);
185 // Nothing should be built since M doesn't dependent on anything.
186 HeaderSearchOptions HSOpts;
187 MInfo->adjustHeaderSearchOptions(HSOpts);
188 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.empty());
190 auto Invocation =
191 buildCompilerInvocation(getInputs("M.cppm", CDB), DiagConsumer);
192 EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
195 TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
196 MockDirectoryCompilationDatabase CDB(TestDir, FS);
198 CDB.ExtraClangFlags.push_back("-invalid-unknown-flag");
200 CDB.addFile("Dep.cppm", R"cpp(
201 export module Dep;
202 )cpp");
204 CDB.addFile("M.cppm", R"cpp(
205 export module M;
206 import Dep;
207 )cpp");
209 // An invalid flag will break the module compilation and the
210 // getRequiredModules would return an empty array
211 auto ProjectModules = CDB.getProjectModules(getFullPath("M.cppm"));
212 EXPECT_TRUE(
213 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
215 // Set the mangler to filter out the invalid flag
216 ProjectModules->setCommandMangler(
217 [](tooling::CompileCommand &Command, PathRef) {
218 auto const It =
219 std::find(Command.CommandLine.begin(), Command.CommandLine.end(),
220 "-invalid-unknown-flag");
221 Command.CommandLine.erase(It);
224 // And now it returns a non-empty list of required modules since the
225 // compilation succeeded
226 EXPECT_FALSE(
227 ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
230 TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
231 MockDirectoryCompilationDatabase CDB(TestDir, FS);
233 CDB.addFile("foo.h", R"cpp(
234 inline void foo() {}
235 )cpp");
237 CDB.addFile("M.cppm", R"cpp(
238 module;
239 #include "foo.h"
240 export module M;
241 )cpp");
243 CDB.addFile("N.cppm", R"cpp(
244 export module N;
245 import :Part;
246 import M;
247 )cpp");
249 CDB.addFile("N-part.cppm", R"cpp(
250 // Different module name with filename intentionally.
251 export module N:Part;
252 )cpp");
254 ModulesBuilder Builder(CDB);
256 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
257 EXPECT_TRUE(NInfo);
259 ParseInputs NInput = getInputs("N.cppm", CDB);
260 std::unique_ptr<CompilerInvocation> Invocation =
261 buildCompilerInvocation(NInput, DiagConsumer);
262 // Test that `PrerequisiteModules::canReuse` works basically.
263 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
266 // Check that
267 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`
268 // can appending HeaderSearchOptions correctly.
269 HeaderSearchOptions HSOpts;
270 NInfo->adjustHeaderSearchOptions(HSOpts);
272 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("M"));
273 EXPECT_TRUE(HSOpts.PrebuiltModuleFiles.count("N:Part"));
277 // Check that
278 // `PrerequisiteModules::adjustHeaderSearchOptions(HeaderSearchOptions&)`
279 // can replace HeaderSearchOptions correctly.
280 HeaderSearchOptions HSOpts;
281 HSOpts.PrebuiltModuleFiles["M"] = "incorrect_path";
282 HSOpts.PrebuiltModuleFiles["N:Part"] = "incorrect_path";
283 NInfo->adjustHeaderSearchOptions(HSOpts);
285 EXPECT_TRUE(StringRef(HSOpts.PrebuiltModuleFiles["M"]).ends_with(".pcm"));
286 EXPECT_TRUE(
287 StringRef(HSOpts.PrebuiltModuleFiles["N:Part"]).ends_with(".pcm"));
291 TEST_F(PrerequisiteModulesTests, ReusabilityTest) {
292 MockDirectoryCompilationDatabase CDB(TestDir, FS);
294 CDB.addFile("foo.h", R"cpp(
295 inline void foo() {}
296 )cpp");
298 CDB.addFile("M.cppm", R"cpp(
299 module;
300 #include "foo.h"
301 export module M;
302 )cpp");
304 CDB.addFile("N.cppm", R"cpp(
305 export module N;
306 import :Part;
307 import M;
308 )cpp");
310 CDB.addFile("N-part.cppm", R"cpp(
311 // Different module name with filename intentionally.
312 export module N:Part;
313 )cpp");
315 ModulesBuilder Builder(CDB);
317 auto NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
318 EXPECT_TRUE(NInfo);
319 EXPECT_TRUE(NInfo);
321 ParseInputs NInput = getInputs("N.cppm", CDB);
322 std::unique_ptr<CompilerInvocation> Invocation =
323 buildCompilerInvocation(NInput, DiagConsumer);
324 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
326 // Test that we can still reuse the NInfo after we touch a unrelated file.
328 CDB.addFile("L.cppm", R"cpp(
329 module;
330 #include "foo.h"
331 export module L;
332 export int ll = 43;
333 )cpp");
334 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
336 CDB.addFile("bar.h", R"cpp(
337 inline void bar() {}
338 inline void bar(int) {}
339 )cpp");
340 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
343 // Test that we can't reuse the NInfo after we touch a related file.
345 CDB.addFile("M.cppm", R"cpp(
346 module;
347 #include "foo.h"
348 export module M;
349 export int mm = 44;
350 )cpp");
351 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
353 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
354 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
356 CDB.addFile("foo.h", R"cpp(
357 inline void foo() {}
358 inline void foo(int) {}
359 )cpp");
360 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
362 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
363 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
366 CDB.addFile("N-part.cppm", R"cpp(
367 export module N:Part;
368 // Intentioned to make it uncompilable.
369 export int NPart = 4LIdjwldijaw
370 )cpp");
371 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
372 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
373 EXPECT_TRUE(NInfo);
374 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
376 CDB.addFile("N-part.cppm", R"cpp(
377 export module N:Part;
378 export int NPart = 43;
379 )cpp");
380 EXPECT_TRUE(NInfo);
381 EXPECT_FALSE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
382 NInfo = Builder.buildPrerequisiteModulesFor(getFullPath("N.cppm"), FS);
383 EXPECT_TRUE(NInfo);
384 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
386 // Test that if we changed the modification time of the file, the module files
387 // info is still reusable if its content doesn't change.
388 CDB.addFile("N-part.cppm", R"cpp(
389 export module N:Part;
390 export int NPart = 43;
391 )cpp");
392 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
394 CDB.addFile("N.cppm", R"cpp(
395 export module N;
396 import :Part;
397 import M;
399 export int nn = 43;
400 )cpp");
401 // NInfo should be reusable after we change its content.
402 EXPECT_TRUE(NInfo->canReuse(*Invocation, FS.view(TestDir)));
405 // An End-to-End test for modules.
406 TEST_F(PrerequisiteModulesTests, ParsedASTTest) {
407 MockDirectoryCompilationDatabase CDB(TestDir, FS);
409 CDB.addFile("A.cppm", R"cpp(
410 export module A;
411 export void printA();
412 )cpp");
414 CDB.addFile("Use.cpp", R"cpp(
415 import A;
416 )cpp");
418 ModulesBuilder Builder(CDB);
420 ParseInputs Use = getInputs("Use.cpp", CDB);
421 Use.ModulesManager = &Builder;
423 std::unique_ptr<CompilerInvocation> CI =
424 buildCompilerInvocation(Use, DiagConsumer);
425 EXPECT_TRUE(CI);
427 auto Preamble =
428 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
429 /*Callback=*/nullptr);
430 EXPECT_TRUE(Preamble);
431 EXPECT_TRUE(Preamble->RequiredModules);
433 auto AST = ParsedAST::build(getFullPath("Use.cpp"), Use, std::move(CI), {},
434 Preamble);
435 EXPECT_TRUE(AST);
437 const NamedDecl &D = findDecl(*AST, "printA");
438 EXPECT_TRUE(D.isFromASTFile());
441 // An end to end test for code complete in modules
442 TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
443 MockDirectoryCompilationDatabase CDB(TestDir, FS);
445 CDB.addFile("A.cppm", R"cpp(
446 export module A;
447 export void printA();
448 )cpp");
450 llvm::StringLiteral UserContents = R"cpp(
451 import A;
452 void func() {
453 print^
455 )cpp";
457 CDB.addFile("Use.cpp", UserContents);
458 Annotations Test(UserContents);
460 ModulesBuilder Builder(CDB);
462 ParseInputs Use = getInputs("Use.cpp", CDB);
463 Use.ModulesManager = &Builder;
465 std::unique_ptr<CompilerInvocation> CI =
466 buildCompilerInvocation(Use, DiagConsumer);
467 EXPECT_TRUE(CI);
469 auto Preamble =
470 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
471 /*Callback=*/nullptr);
472 EXPECT_TRUE(Preamble);
473 EXPECT_TRUE(Preamble->RequiredModules);
475 auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),
476 Preamble.get(), Use, {});
477 EXPECT_FALSE(Result.Completions.empty());
478 EXPECT_EQ(Result.Completions[0].Name, "printA");
481 TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
482 MockDirectoryCompilationDatabase CDB(TestDir, FS);
484 CDB.addFile("A.cppm", R"cpp(
485 export module A;
486 export void printA(int a);
487 )cpp");
489 llvm::StringLiteral UserContents = R"cpp(
490 import A;
491 void func() {
492 printA(^);
494 )cpp";
496 CDB.addFile("Use.cpp", UserContents);
497 Annotations Test(UserContents);
499 ModulesBuilder Builder(CDB);
501 ParseInputs Use = getInputs("Use.cpp", CDB);
502 Use.ModulesManager = &Builder;
504 std::unique_ptr<CompilerInvocation> CI =
505 buildCompilerInvocation(Use, DiagConsumer);
506 EXPECT_TRUE(CI);
508 auto Preamble =
509 buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
510 /*Callback=*/nullptr);
511 EXPECT_TRUE(Preamble);
512 EXPECT_TRUE(Preamble->RequiredModules);
514 auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(),
515 *Preamble.get(), Use, MarkupKind::PlainText);
516 EXPECT_FALSE(Result.signatures.empty());
517 EXPECT_EQ(Result.signatures[0].label, "printA(int a) -> void");
518 EXPECT_EQ(Result.signatures[0].parameters[0].labelString, "int a");
521 TEST_F(PrerequisiteModulesTests, ReusablePrerequisiteModulesTest) {
522 MockDirectoryCompilationDatabase CDB(TestDir, FS);
524 CDB.addFile("M.cppm", R"cpp(
525 export module M;
526 export int M = 43;
527 )cpp");
528 CDB.addFile("A.cppm", R"cpp(
529 export module A;
530 import M;
531 export int A = 43 + M;
532 )cpp");
533 CDB.addFile("B.cppm", R"cpp(
534 export module B;
535 import M;
536 export int B = 44 + M;
537 )cpp");
539 ModulesBuilder Builder(CDB);
541 auto AInfo = Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
542 EXPECT_TRUE(AInfo);
543 auto BInfo = Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);
544 EXPECT_TRUE(BInfo);
545 HeaderSearchOptions HSOptsA(TestDir);
546 HeaderSearchOptions HSOptsB(TestDir);
547 AInfo->adjustHeaderSearchOptions(HSOptsA);
548 BInfo->adjustHeaderSearchOptions(HSOptsB);
550 EXPECT_FALSE(HSOptsA.PrebuiltModuleFiles.empty());
551 EXPECT_FALSE(HSOptsB.PrebuiltModuleFiles.empty());
553 // Check that we're reusing the module files.
554 EXPECT_EQ(HSOptsA.PrebuiltModuleFiles, HSOptsB.PrebuiltModuleFiles);
556 // Update M.cppm to check if the modules builder can update correctly.
557 CDB.addFile("M.cppm", R"cpp(
558 export module M;
559 export constexpr int M = 43;
560 )cpp");
562 ParseInputs AUse = getInputs("A.cppm", CDB);
563 AUse.ModulesManager = &Builder;
564 std::unique_ptr<CompilerInvocation> AInvocation =
565 buildCompilerInvocation(AUse, DiagConsumer);
566 EXPECT_FALSE(AInfo->canReuse(*AInvocation, FS.view(TestDir)));
568 ParseInputs BUse = getInputs("B.cppm", CDB);
569 AUse.ModulesManager = &Builder;
570 std::unique_ptr<CompilerInvocation> BInvocation =
571 buildCompilerInvocation(BUse, DiagConsumer);
572 EXPECT_FALSE(BInfo->canReuse(*BInvocation, FS.view(TestDir)));
574 auto NewAInfo =
575 Builder.buildPrerequisiteModulesFor(getFullPath("A.cppm"), FS);
576 auto NewBInfo =
577 Builder.buildPrerequisiteModulesFor(getFullPath("B.cppm"), FS);
578 EXPECT_TRUE(NewAInfo);
579 EXPECT_TRUE(NewBInfo);
580 HeaderSearchOptions NewHSOptsA(TestDir);
581 HeaderSearchOptions NewHSOptsB(TestDir);
582 NewAInfo->adjustHeaderSearchOptions(NewHSOptsA);
583 NewBInfo->adjustHeaderSearchOptions(NewHSOptsB);
585 EXPECT_FALSE(NewHSOptsA.PrebuiltModuleFiles.empty());
586 EXPECT_FALSE(NewHSOptsB.PrebuiltModuleFiles.empty());
588 EXPECT_EQ(NewHSOptsA.PrebuiltModuleFiles, NewHSOptsB.PrebuiltModuleFiles);
589 // Check that we didn't reuse the old and stale module files.
590 EXPECT_NE(NewHSOptsA.PrebuiltModuleFiles, HSOptsA.PrebuiltModuleFiles);
593 } // namespace
594 } // namespace clang::clangd
596 #endif