|
6 | 6 | #include "swift/Basic/SourceManager.h"
|
7 | 7 | #include "swift/ClangImporter/ClangImporter.h"
|
8 | 8 | #include "swift/ClangImporter/ClangImporterOptions.h"
|
| 9 | +#include "clang/Basic/FileManager.h" |
| 10 | +#include "clang/Basic/FileSystemOptions.h" |
| 11 | +#include "clang/Basic/FileSystemStatCache.h" |
| 12 | +#include "clang/Frontend/CompilerInstance.h" |
9 | 13 | #include "llvm/Support/FileSystem.h"
|
10 | 14 | #include "llvm/Support/Path.h"
|
11 | 15 | #include "llvm/Support/raw_ostream.h"
|
@@ -54,7 +58,6 @@ TEST(ClangImporterTest, emitPCHInMemory) {
|
54 | 58 | // Create the includes.
|
55 | 59 | std::string include = createFilename(temp, "include");
|
56 | 60 | ASSERT_FALSE(llvm::sys::fs::create_directory(include));
|
57 |
| - options.ExtraArgs.emplace_back("-nosysteminc"); |
58 | 61 | options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str());
|
59 | 62 | ASSERT_FALSE(emitFileWithContents(include, "module.modulemap",
|
60 | 63 | "module A {\n"
|
@@ -88,3 +91,105 @@ TEST(ClangImporterTest, emitPCHInMemory) {
|
88 | 91 | ASSERT_FALSE(emitFileWithContents(PCH, "garbage"));
|
89 | 92 | ASSERT_TRUE(importer->canReadPCH(PCH));
|
90 | 93 | }
|
| 94 | + |
| 95 | +class ForgetfulStatCache : public clang::FileSystemStatCache { |
| 96 | +private: |
| 97 | + std::unique_ptr<clang::MemorizeStatCalls> RealStatCache; |
| 98 | + |
| 99 | +public: |
| 100 | + ForgetfulStatCache() { |
| 101 | + RealStatCache = std::make_unique<clang::MemorizeStatCalls>(); |
| 102 | + } |
| 103 | + ~ForgetfulStatCache() = default; |
| 104 | + |
| 105 | + std::error_code getStat(StringRef Path, llvm::vfs::Status &Status, |
| 106 | + bool isFile, |
| 107 | + std::unique_ptr<llvm::vfs::File> *F, |
| 108 | + llvm::vfs::FileSystem &FS) override { |
| 109 | + if (llvm::sys::path::extension(Path) == ".pcm") { |
| 110 | + const auto UID = llvm::sys::fs::UniqueID(1, std::numeric_limits<uint64_t>::max()); |
| 111 | + Status = llvm::vfs::Status(Path, UID, |
| 112 | + /*MTime*/{}, /*User*/0, /*Group*/0, |
| 113 | + /*Size*/0, |
| 114 | + llvm::sys::fs::file_type::regular_file, |
| 115 | + llvm::sys::fs::perms::all_all); |
| 116 | + return std::error_code(); |
| 117 | + } |
| 118 | + |
| 119 | + return clang::FileSystemStatCache::get(Path, Status, isFile, F, |
| 120 | + RealStatCache.get(), FS); |
| 121 | + } |
| 122 | +}; |
| 123 | + |
| 124 | +TEST(ClangImporterTest, missingSubmodule) { |
| 125 | + // Create a temporary cache on disk and clean it up at the end. |
| 126 | + ClangImporterOptions options; |
| 127 | + SmallString<256> temp; |
| 128 | + ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory( |
| 129 | + "ClangImporterTest.missingSubmodule", temp)); |
| 130 | + SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); }; |
| 131 | + |
| 132 | + // Create a cache subdirectory for the modules and PCH. |
| 133 | + std::string cache = createFilename(temp, "cache"); |
| 134 | + ASSERT_FALSE(llvm::sys::fs::create_directory(cache)); |
| 135 | + options.ModuleCachePath = cache; |
| 136 | + options.PrecompiledHeaderOutputDir = cache; |
| 137 | + |
| 138 | + // Create the includes. |
| 139 | + std::string include1 = createFilename(temp, "include1"); |
| 140 | + ASSERT_FALSE(llvm::sys::fs::create_directory(include1)); |
| 141 | + |
| 142 | + std::string include2 = createFilename(temp, "include2"); |
| 143 | + ASSERT_FALSE(llvm::sys::fs::create_directory(include2)); |
| 144 | + |
| 145 | + options.ExtraArgs.emplace_back((llvm::Twine("-I") + include1).str()); |
| 146 | + options.ExtraArgs.emplace_back((llvm::Twine("-I") + include2).str()); |
| 147 | + options.ExtraArgs.emplace_back("-DEXTRA_C_DEFINE=2"); |
| 148 | + |
| 149 | + ASSERT_FALSE(emitFileWithContents(include1, "module.modulemap", |
| 150 | + "module CLib1 {\n" |
| 151 | + " umbrella header \"" + include1 + "/Clib1.h\"\n" |
| 152 | + " export * \n" |
| 153 | + "}\n" |
| 154 | + "module CLib2 {\n" |
| 155 | + " umbrella header \"" + include2 + "/Clib2.h\"\n" |
| 156 | + " export * \n" |
| 157 | + "}\n")); |
| 158 | + ASSERT_FALSE(emitFileWithContents(include1, "CLib1.h", |
| 159 | + "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" |
| 160 | + "#error \"unexpected compiler flags\"\n" |
| 161 | + "#endif\n" |
| 162 | + "void foo(void);\n")); |
| 163 | + ASSERT_FALSE(emitFileWithContents(include2, "CLib2.h", |
| 164 | + "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" |
| 165 | + "#error \"unexpected compiler flags\"\n" |
| 166 | + "#endif\n" |
| 167 | + "void foo(void);\n")); |
| 168 | + |
| 169 | + // Set up the importer. |
| 170 | + swift::LangOptions langOpts; |
| 171 | + langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); |
| 172 | + swift::TypeCheckerOptions typeckOpts; |
| 173 | + INITIALIZE_LLVM(); |
| 174 | + swift::SearchPathOptions searchPathOpts; |
| 175 | + swift::SourceManager sourceMgr; |
| 176 | + swift::DiagnosticEngine diags(sourceMgr); |
| 177 | + std::unique_ptr<ASTContext> context( |
| 178 | + ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); |
| 179 | + auto importer = ClangImporter::create(*context, options); |
| 180 | + |
| 181 | + // Install a stats cache that conveniently "forgets" that PCMs have different |
| 182 | + // underlying FileEntry values. |
| 183 | + importer->getClangInstance() |
| 184 | + .getFileManager() |
| 185 | + .setStatCache(std::make_unique<ForgetfulStatCache>()); |
| 186 | + |
| 187 | + context->addModuleLoader(std::move(importer)); |
| 188 | + |
| 189 | + auto CLib1 = context->getIdentifier("CLib1"); |
| 190 | + auto CLib2 = context->getIdentifier("CLib2"); |
| 191 | + // The first load succeeds and primes the ModuleManager with PCM data. |
| 192 | + (void)context->getModule({ Located<Identifier>{ CLib1, {} } }); |
| 193 | + // The second load fails because we collide in the ModuleManager. |
| 194 | + ASSERT_TRUE(context->getModule({ Located<Identifier>{ CLib2, {} } }) == nullptr); |
| 195 | +} |
0 commit comments