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