Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Serialization / ModuleCacheTest.cpp
blobc3e347ffec660c6095e250cfd5a507fcbef5661d
1 //===- unittests/Serialization/ModuleCacheTest.cpp - CI tests -------------===//
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 //===----------------------------------------------------------------------===//
9 #include "clang/Basic/FileManager.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/CompilerInvocation.h"
12 #include "clang/Frontend/FrontendActions.h"
13 #include "clang/Frontend/Utils.h"
14 #include "clang/Lex/HeaderSearch.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/VirtualFileSystem.h"
18 #include "llvm/Support/raw_ostream.h"
20 #include "gtest/gtest.h"
22 using namespace llvm;
23 using namespace clang;
25 namespace {
27 class ModuleCacheTest : public ::testing::Test {
28 void SetUp() override {
29 ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir));
31 ModuleCachePath = SmallString<256>(TestDir);
32 sys::path::append(ModuleCachePath, "mcp");
33 ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath));
36 void TearDown() override { sys::fs::remove_directories(TestDir); }
38 public:
39 SmallString<256> TestDir;
40 SmallString<256> ModuleCachePath;
42 void addFile(StringRef Path, StringRef Contents) {
43 ASSERT_FALSE(sys::path::is_absolute(Path));
45 SmallString<256> AbsPath(TestDir);
46 sys::path::append(AbsPath, Path);
48 std::error_code EC;
49 ASSERT_FALSE(
50 sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
51 llvm::raw_fd_ostream OS(AbsPath, EC);
52 ASSERT_FALSE(EC);
53 OS << Contents;
56 void addDuplicateFrameworks() {
57 addFile("test.m", R"cpp(
58 @import Top;
59 )cpp");
61 addFile("frameworks/Top.framework/Headers/top.h", R"cpp(
62 @import M;
63 )cpp");
64 addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp(
65 framework module Top [system] {
66 header "top.h"
67 export *
69 )cpp");
71 addFile("frameworks/M.framework/Headers/m.h", R"cpp(
72 void foo();
73 )cpp");
74 addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp(
75 framework module M [system] {
76 header "m.h"
77 export *
79 )cpp");
81 addFile("frameworks2/M.framework/Headers/m.h", R"cpp(
82 void foo();
83 )cpp");
84 addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp(
85 framework module M [system] {
86 header "m.h"
87 export *
89 )cpp");
93 TEST_F(ModuleCacheTest, CachedModuleNewPath) {
94 addDuplicateFrameworks();
96 SmallString<256> MCPArg("-fmodules-cache-path=");
97 MCPArg.append(ModuleCachePath);
98 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
99 CompilerInstance::createDiagnostics(new DiagnosticOptions());
100 CreateInvocationOptions CIOpts;
101 CIOpts.Diags = Diags;
102 CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
104 // First run should pass with no errors
105 const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
106 MCPArg.c_str(), "-working-directory", TestDir.c_str(),
107 "test.m"};
108 std::shared_ptr<CompilerInvocation> Invocation =
109 createInvocation(Args, CIOpts);
110 ASSERT_TRUE(Invocation);
111 CompilerInstance Instance;
112 Instance.setDiagnostics(Diags.get());
113 Instance.setInvocation(Invocation);
114 SyntaxOnlyAction Action;
115 ASSERT_TRUE(Instance.ExecuteAction(Action));
116 ASSERT_FALSE(Diags->hasErrorOccurred());
118 // Now add `frameworks2` to the search path. `Top.pcm` will have a reference
119 // to the `M` from `frameworks`, but a search will find the `M` from
120 // `frameworks2` - causing a mismatch and it to be considered out of date.
122 // Normally this would be fine - `M` and the modules it depends on would be
123 // rebuilt. However, since we have a shared module cache and thus an already
124 // finalized `Top`, recompiling `Top` will cause the existing module to be
125 // removed from the cache, causing possible crashed if it is ever used.
127 // Make sure that an error occurs instead.
128 const char *Args2[] = {"clang", "-fmodules", "-Fframeworks2",
129 "-Fframeworks", MCPArg.c_str(), "-working-directory",
130 TestDir.c_str(), "test.m"};
131 std::shared_ptr<CompilerInvocation> Invocation2 =
132 createInvocation(Args2, CIOpts);
133 ASSERT_TRUE(Invocation2);
134 CompilerInstance Instance2(Instance.getPCHContainerOperations(),
135 &Instance.getModuleCache());
136 Instance2.setDiagnostics(Diags.get());
137 Instance2.setInvocation(Invocation2);
138 SyntaxOnlyAction Action2;
139 ASSERT_FALSE(Instance2.ExecuteAction(Action2));
140 ASSERT_TRUE(Diags->hasErrorOccurred());
143 TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) {
144 addDuplicateFrameworks();
146 SmallString<256> MCPArg("-fmodules-cache-path=");
147 MCPArg.append(ModuleCachePath);
148 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
149 CompilerInstance::createDiagnostics(new DiagnosticOptions());
150 CreateInvocationOptions CIOpts;
151 CIOpts.Diags = Diags;
152 CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
154 // First run should pass with no errors
155 const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
156 MCPArg.c_str(), "-working-directory", TestDir.c_str(),
157 "test.m"};
158 std::shared_ptr<CompilerInvocation> Invocation =
159 createInvocation(Args, CIOpts);
160 ASSERT_TRUE(Invocation);
161 CompilerInstance Instance;
162 Instance.setDiagnostics(Diags.get());
163 Instance.setInvocation(Invocation);
164 SyntaxOnlyAction Action;
165 ASSERT_TRUE(Instance.ExecuteAction(Action));
166 ASSERT_FALSE(Diags->hasErrorOccurred());
168 // Same as `CachedModuleNewPath` but while allowing errors. This is a hard
169 // failure where the module wasn't created, so it should still fail.
170 const char *Args2[] = {
171 "clang", "-fmodules", "-Fframeworks2",
172 "-Fframeworks", MCPArg.c_str(), "-working-directory",
173 TestDir.c_str(), "-Xclang", "-fallow-pcm-with-compiler-errors",
174 "test.m"};
175 std::shared_ptr<CompilerInvocation> Invocation2 =
176 createInvocation(Args2, CIOpts);
177 ASSERT_TRUE(Invocation2);
178 CompilerInstance Instance2(Instance.getPCHContainerOperations(),
179 &Instance.getModuleCache());
180 Instance2.setDiagnostics(Diags.get());
181 Instance2.setInvocation(Invocation2);
182 SyntaxOnlyAction Action2;
183 ASSERT_FALSE(Instance2.ExecuteAction(Action2));
184 ASSERT_TRUE(Diags->hasErrorOccurred());
187 } // anonymous namespace