xref: /llvm-project/clang/unittests/Serialization/PreambleInNamedModulesTest.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
1 //===- unittests/Serialization/PreambleInNamedModulesTest.cpp -------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Frontend/CompilerInstance.h"
10 #include "clang/Frontend/CompilerInvocation.h"
11 #include "clang/Frontend/FrontendActions.h"
12 #include "clang/Frontend/PrecompiledPreamble.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/raw_ostream.h"
16 
17 #include "gtest/gtest.h"
18 
19 using namespace llvm;
20 using namespace clang;
21 
22 namespace {
23 
24 class PreambleInNamedModulesTest : public ::testing::Test {
25   void SetUp() override {
26     ASSERT_FALSE(sys::fs::createUniqueDirectory("modules-test", TestDir));
27   }
28 
29   void TearDown() override { sys::fs::remove_directories(TestDir); }
30 
31 public:
32   using PathType = SmallString<256>;
33 
34   PathType TestDir;
35 
36   void addFile(StringRef Path, StringRef Contents, PathType &AbsPath) {
37     ASSERT_FALSE(sys::path::is_absolute(Path));
38 
39     AbsPath = TestDir;
40     sys::path::append(AbsPath, Path);
41 
42     ASSERT_FALSE(
43         sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
44 
45     std::error_code EC;
46     llvm::raw_fd_ostream OS(AbsPath, EC);
47     ASSERT_FALSE(EC);
48     OS << Contents;
49   }
50 
51   void addFile(StringRef Path, StringRef Contents) {
52     PathType UnusedAbsPath;
53     addFile(Path, Contents, UnusedAbsPath);
54   }
55 };
56 
57 // Testing that the use of Preamble in named modules can work basically.
58 // See https://github.com/llvm/llvm-project/issues/80570
59 TEST_F(PreambleInNamedModulesTest, BasicTest) {
60   addFile("foo.h", R"cpp(
61 enum class E {
62     A,
63     B,
64     C,
65     D
66 };
67   )cpp");
68 
69   PathType MainFilePath;
70   addFile("A.cppm", R"cpp(
71 module;
72 #include "foo.h"
73 export module A;
74 export using ::E;
75   )cpp",
76           MainFilePath);
77 
78   IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
79       llvm::vfs::createPhysicalFileSystem();
80   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
81       CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions());
82 
83   CreateInvocationOptions CIOpts;
84   CIOpts.Diags = Diags;
85   CIOpts.VFS = VFS;
86 
87   const char *Args[] = {"clang++", "-std=c++20", "-working-directory",
88                         TestDir.c_str(), MainFilePath.c_str()};
89   std::shared_ptr<CompilerInvocation> Invocation =
90       createInvocation(Args, CIOpts);
91   ASSERT_TRUE(Invocation);
92 
93   llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ContentsBuffer =
94       llvm::MemoryBuffer::getFile(MainFilePath, /*IsText=*/true);
95   EXPECT_TRUE(ContentsBuffer);
96   std::unique_ptr<MemoryBuffer> Buffer = std::move(*ContentsBuffer);
97 
98   PreambleBounds Bounds =
99       ComputePreambleBounds(Invocation->getLangOpts(), *Buffer, 0);
100 
101   PreambleCallbacks Callbacks;
102   llvm::ErrorOr<PrecompiledPreamble> BuiltPreamble = PrecompiledPreamble::Build(
103       *Invocation, Buffer.get(), Bounds, *Diags, VFS,
104       std::make_shared<PCHContainerOperations>(),
105       /*StoreInMemory=*/false, /*StoragePath=*/TestDir, Callbacks);
106 
107   ASSERT_FALSE(Diags->hasErrorOccurred());
108 
109   EXPECT_TRUE(BuiltPreamble);
110   EXPECT_TRUE(BuiltPreamble->CanReuse(*Invocation, *Buffer, Bounds, *VFS));
111   BuiltPreamble->OverridePreamble(*Invocation, VFS, Buffer.get());
112 
113   auto Clang = std::make_unique<CompilerInstance>(
114       std::make_shared<PCHContainerOperations>());
115   Clang->setInvocation(std::move(Invocation));
116   Clang->setDiagnostics(Diags.get());
117 
118   if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
119           Clang->getInvocation(), Clang->getDiagnostics(), VFS))
120     VFS = VFSWithRemapping;
121 
122   Clang->createFileManager(VFS);
123   EXPECT_TRUE(Clang->createTarget());
124 
125   Buffer.release();
126 
127   SyntaxOnlyAction Action;
128   EXPECT_TRUE(Clang->ExecuteAction(Action));
129   EXPECT_FALSE(Clang->getDiagnosticsPtr()->hasErrorOccurred());
130 }
131 
132 } // namespace
133