1 //===-- CppModuleConfigurationTest.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 "Plugins/ExpressionParser/Clang/CppModuleConfiguration.h"
10 #include "Plugins/ExpressionParser/Clang/ClangHost.h"
11 #include "TestingSupport/SubsystemRAII.h"
12 #include "lldb/Host/FileSystem.h"
13 #include "lldb/Host/HostInfo.h"
14 #include "llvm/Support/SmallVectorMemoryBuffer.h"
15
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18
19 using namespace lldb_private;
20
21 namespace {
22 struct CppModuleConfigurationTest : public testing::Test {
23 llvm::MemoryBufferRef m_empty_buffer;
24 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> m_fs;
25
CppModuleConfigurationTest__anon6affa9db0111::CppModuleConfigurationTest26 CppModuleConfigurationTest()
27 : m_empty_buffer("", "<empty buffer>"),
28 m_fs(new llvm::vfs::InMemoryFileSystem()) {}
29
SetUp__anon6affa9db0111::CppModuleConfigurationTest30 void SetUp() override {
31 FileSystem::Initialize(m_fs);
32 HostInfo::Initialize();
33 }
34
TearDown__anon6affa9db0111::CppModuleConfigurationTest35 void TearDown() override {
36 HostInfo::Terminate();
37 FileSystem::Terminate();
38 }
39
40 /// Utility function turning a list of paths into a FileSpecList.
makeFiles__anon6affa9db0111::CppModuleConfigurationTest41 FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
42 FileSpecList result;
43 for (const std::string &path : paths) {
44 result.Append(FileSpec(path, FileSpec::Style::posix));
45 if (!m_fs->addFileNoOwn(path, static_cast<time_t>(0), m_empty_buffer))
46 llvm_unreachable("Invalid test configuration?");
47 }
48 return result;
49 }
50 };
51 } // namespace
52
53 /// Returns the Clang resource include directory.
ResourceInc()54 static std::string ResourceInc() {
55 llvm::SmallString<256> resource_dir;
56 llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
57 "include");
58 return std::string(resource_dir);
59 }
60
61
TEST_F(CppModuleConfigurationTest,Linux)62 TEST_F(CppModuleConfigurationTest, Linux) {
63 // Test the average Linux configuration.
64
65 std::string usr = "/usr/include";
66 std::string libcpp = "/usr/include/c++/v1";
67 std::vector<std::string> files = {// C library
68 usr + "/stdio.h",
69 // C++ library
70 libcpp + "/vector",
71 libcpp + "/module.modulemap"};
72 CppModuleConfiguration config(makeFiles(files));
73 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
74 EXPECT_THAT(config.GetIncludeDirs(),
75 testing::ElementsAre(libcpp, ResourceInc(), usr));
76 }
77
TEST_F(CppModuleConfigurationTest,Sysroot)78 TEST_F(CppModuleConfigurationTest, Sysroot) {
79 // Test that having a sysroot for the whole system works fine.
80
81 std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";
82 std::string usr = "/home/user/sysroot/usr/include";
83 std::vector<std::string> files = {// C library
84 usr + "/stdio.h",
85 // C++ library
86 libcpp + "/vector",
87 libcpp + "/module.modulemap"};
88 CppModuleConfiguration config(makeFiles(files));
89 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
90 EXPECT_THAT(config.GetIncludeDirs(),
91 testing::ElementsAre(libcpp, ResourceInc(), usr));
92 }
93
TEST_F(CppModuleConfigurationTest,LinuxLocalLibCpp)94 TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
95 // Test that a locally build libc++ is detected.
96
97 std::string usr = "/usr/include";
98 std::string libcpp = "/home/user/llvm-build/include/c++/v1";
99 std::vector<std::string> files = {// C library
100 usr + "/stdio.h",
101 // C++ library
102 libcpp + "/vector",
103 libcpp + "/module.modulemap"};
104 CppModuleConfiguration config(makeFiles(files));
105 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
106 EXPECT_THAT(config.GetIncludeDirs(),
107 testing::ElementsAre(libcpp, ResourceInc(), usr));
108 }
109
TEST_F(CppModuleConfigurationTest,UnrelatedLibrary)110 TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
111 // Test that having an unrelated library in /usr/include doesn't break.
112
113 std::string usr = "/usr/include";
114 std::string libcpp = "/home/user/llvm-build/include/c++/v1";
115 std::vector<std::string> files = {// C library
116 usr + "/stdio.h",
117 // unrelated library
118 usr + "/boost/vector",
119 // C++ library
120 libcpp + "/vector",
121 libcpp + "/module.modulemap"};
122 CppModuleConfiguration config(makeFiles(files));
123 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
124 EXPECT_THAT(config.GetIncludeDirs(),
125 testing::ElementsAre(libcpp, ResourceInc(), usr));
126 }
127
TEST_F(CppModuleConfigurationTest,Xcode)128 TEST_F(CppModuleConfigurationTest, Xcode) {
129 // Test detection of libc++ coming from Xcode with generic platform names.
130
131 std::string p = "/Applications/Xcode.app/Contents/Developer/";
132 std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";
133 std::string usr =
134 p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
135 std::vector<std::string> files = {
136 // C library
137 usr + "/stdio.h",
138 // C++ library
139 libcpp + "/vector",
140 libcpp + "/module.modulemap",
141 };
142 CppModuleConfiguration config(makeFiles(files));
143 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
144 EXPECT_THAT(config.GetIncludeDirs(),
145 testing::ElementsAre(libcpp, ResourceInc(), usr));
146 }
147
TEST_F(CppModuleConfigurationTest,LibCppV2)148 TEST_F(CppModuleConfigurationTest, LibCppV2) {
149 // Test that a "v2" of libc++ is still correctly detected.
150
151 std::string libcpp = "/usr/include/c++/v2";
152 std::vector<std::string> files = {// C library
153 "/usr/include/stdio.h",
154 // C++ library
155 libcpp + "/vector",
156 libcpp + "/module.modulemap"};
157 CppModuleConfiguration config(makeFiles(files));
158 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
159 EXPECT_THAT(config.GetIncludeDirs(),
160 testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
161 "/usr/include"));
162 }
163
TEST_F(CppModuleConfigurationTest,UnknownLibCppFile)164 TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
165 // Test that having some unknown file in the libc++ path doesn't break
166 // anything.
167
168 std::string libcpp = "/usr/include/c++/v1";
169 std::vector<std::string> files = {// C library
170 "/usr/include/stdio.h",
171 // C++ library
172 libcpp + "/non_existing_file",
173 libcpp + "/module.modulemap",
174 libcpp + "/vector"};
175 CppModuleConfiguration config(makeFiles(files));
176 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
177 EXPECT_THAT(config.GetIncludeDirs(),
178 testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
179 "/usr/include"));
180 }
181
TEST_F(CppModuleConfigurationTest,MissingUsrInclude)182 TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
183 // Test that we don't load 'std' if we can't find the C standard library.
184
185 std::string libcpp = "/usr/include/c++/v1";
186 std::vector<std::string> files = {// C++ library
187 libcpp + "/vector",
188 libcpp + "/module.modulemap"};
189 CppModuleConfiguration config(makeFiles(files));
190 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
191 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
192 }
193
TEST_F(CppModuleConfigurationTest,MissingLibCpp)194 TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
195 // Test that we don't load 'std' if we don't have a libc++.
196
197 std::string usr = "/usr/include";
198 std::vector<std::string> files = {
199 // C library
200 usr + "/stdio.h",
201 };
202 CppModuleConfiguration config(makeFiles(files));
203 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
204 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
205 }
206
TEST_F(CppModuleConfigurationTest,IgnoreLibStdCpp)207 TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
208 // Test that we don't do anything bad when we encounter libstdc++ paths.
209
210 std::string usr = "/usr/include";
211 std::vector<std::string> files = {
212 // C library
213 usr + "/stdio.h",
214 // C++ library
215 usr + "/c++/8.0.1/vector",
216 };
217 CppModuleConfiguration config(makeFiles(files));
218 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
219 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
220 }
221
TEST_F(CppModuleConfigurationTest,AmbiguousCLib)222 TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
223 // Test that we don't do anything when we are not sure where the
224 // right C standard library is.
225
226 std::string usr1 = "/usr/include";
227 std::string usr2 = "/usr/include/other/path";
228 std::string libcpp = usr1 + "c++/v1";
229 std::vector<std::string> files = {
230 // First C library
231 usr1 + "/stdio.h",
232 // Second C library
233 usr2 + "/stdio.h",
234 // C++ library
235 libcpp + "/vector",
236 libcpp + "/module.modulemap",
237 };
238 CppModuleConfiguration config(makeFiles(files));
239 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
240 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
241 }
242
TEST_F(CppModuleConfigurationTest,AmbiguousLibCpp)243 TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
244 // Test that we don't do anything when we are not sure where the
245 // right libc++ is.
246
247 std::string usr = "/usr/include";
248 std::string libcpp1 = usr + "c++/v1";
249 std::string libcpp2 = usr + "c++/v2";
250 std::vector<std::string> files = {
251 // C library
252 usr + "/stdio.h",
253 // First C++ library
254 libcpp1 + "/vector",
255 libcpp1 + "/module.modulemap",
256 // Second C++ library
257 libcpp2 + "/vector",
258 libcpp2 + "/module.modulemap",
259 };
260 CppModuleConfiguration config(makeFiles(files));
261 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
262 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
263 }
264