[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / clangd / unittests / CompileCommandsTests.cpp
blobe8cbaa2bba32d68105260eeb0b5ad2b72d7f7532
1 //===-- CompileCommandsTests.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 //===----------------------------------------------------------------------===//
9 #include "CompileCommands.h"
10 #include "Config.h"
11 #include "TestFS.h"
12 #include "support/Context.h"
14 #include "clang/Tooling/ArgumentsAdjusters.h"
15 #include "llvm/ADT/ArrayRef.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/ScopeExit.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/Process.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
27 namespace clang {
28 namespace clangd {
29 namespace {
31 using ::testing::_;
32 using ::testing::Contains;
33 using ::testing::ElementsAre;
34 using ::testing::HasSubstr;
35 using ::testing::Not;
37 // Sadly, CommandMangler::detect(), which contains much of the logic, is
38 // a bunch of untested integration glue. We test the string manipulation here
39 // assuming its results are correct.
41 // Make use of all features and assert the exact command we get out.
42 // Other tests just verify presence/absence of certain args.
43 TEST(CommandMangler, Everything) {
44 auto Mangler = CommandMangler::forTests();
45 Mangler.ClangPath = testPath("fake/clang");
46 Mangler.ResourceDir = testPath("fake/resources");
47 Mangler.Sysroot = testPath("fake/sysroot");
48 std::vector<std::string> Cmd = {"clang++", "--", "foo.cc", "bar.cc"};
49 Mangler.adjust(Cmd, "foo.cc");
50 EXPECT_THAT(Cmd, ElementsAre(testPath("fake/clang++"),
51 "-resource-dir=" + testPath("fake/resources"),
52 "-isysroot", testPath("fake/sysroot"), "--",
53 "foo.cc"));
56 TEST(CommandMangler, FilenameMismatch) {
57 auto Mangler = CommandMangler::forTests();
58 Mangler.ClangPath = testPath("clang");
59 // Our compile flags refer to foo.cc...
60 std::vector<std::string> Cmd = {"clang", "foo.cc"};
61 // but we're applying it to foo.h...
62 Mangler.adjust(Cmd, "foo.h");
63 // so transferCompileCommand should add -x c++-header to preserve semantics.
64 EXPECT_THAT(
65 Cmd, ElementsAre(testPath("clang"), "-x", "c++-header", "--", "foo.h"));
68 TEST(CommandMangler, ResourceDir) {
69 auto Mangler = CommandMangler::forTests();
70 Mangler.ResourceDir = testPath("fake/resources");
71 std::vector<std::string> Cmd = {"clang++", "foo.cc"};
72 Mangler.adjust(Cmd, "foo.cc");
73 EXPECT_THAT(Cmd, Contains("-resource-dir=" + testPath("fake/resources")));
76 TEST(CommandMangler, Sysroot) {
77 auto Mangler = CommandMangler::forTests();
78 Mangler.Sysroot = testPath("fake/sysroot");
80 std::vector<std::string> Cmd = {"clang++", "foo.cc"};
81 Mangler.adjust(Cmd, "foo.cc");
82 EXPECT_THAT(llvm::join(Cmd, " "),
83 HasSubstr("-isysroot " + testPath("fake/sysroot")));
86 TEST(CommandMangler, ClangPath) {
87 auto Mangler = CommandMangler::forTests();
88 Mangler.ClangPath = testPath("fake/clang");
90 std::vector<std::string> Cmd = {"clang++", "foo.cc"};
91 Mangler.adjust(Cmd, "foo.cc");
92 EXPECT_EQ(testPath("fake/clang++"), Cmd.front());
94 Cmd = {"unknown-binary", "foo.cc"};
95 Mangler.adjust(Cmd, "foo.cc");
96 EXPECT_EQ(testPath("fake/unknown-binary"), Cmd.front());
98 Cmd = {testPath("path/clang++"), "foo.cc"};
99 Mangler.adjust(Cmd, "foo.cc");
100 EXPECT_EQ(testPath("path/clang++"), Cmd.front());
102 Cmd = {"foo/unknown-binary", "foo.cc"};
103 Mangler.adjust(Cmd, "foo.cc");
104 EXPECT_EQ("foo/unknown-binary", Cmd.front());
107 // Only run the PATH/symlink resolving test on unix, we need to fiddle
108 // with permissions and environment variables...
109 #ifdef LLVM_ON_UNIX
110 MATCHER(ok, "") {
111 if (arg) {
112 *result_listener << arg.message();
113 return false;
115 return true;
118 TEST(CommandMangler, ClangPathResolve) {
119 // Set up filesystem:
120 // /temp/
121 // bin/
122 // foo -> temp/lib/bar
123 // lib/
124 // bar
125 llvm::SmallString<256> TempDir;
126 ASSERT_THAT(llvm::sys::fs::createUniqueDirectory("ClangPathResolve", TempDir),
127 ok());
128 // /var/tmp is a symlink on Mac. Resolve it so we're asserting the right path.
129 ASSERT_THAT(llvm::sys::fs::real_path(TempDir.str(), TempDir), ok());
130 auto CleanDir = llvm::make_scope_exit(
131 [&] { llvm::sys::fs::remove_directories(TempDir); });
132 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/bin"), ok());
133 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/lib"), ok());
134 int FD;
135 ASSERT_THAT(llvm::sys::fs::openFileForWrite(TempDir + "/lib/bar", FD), ok());
136 ASSERT_THAT(llvm::sys::Process::SafelyCloseFileDescriptor(FD), ok());
137 ::chmod((TempDir + "/lib/bar").str().c_str(), 0755); // executable
138 ASSERT_THAT(
139 llvm::sys::fs::create_link(TempDir + "/lib/bar", TempDir + "/bin/foo"),
140 ok());
142 // Test the case where the driver is an absolute path to a symlink.
143 auto Mangler = CommandMangler::forTests();
144 Mangler.ClangPath = testPath("fake/clang");
145 std::vector<std::string> Cmd = {(TempDir + "/bin/foo").str(), "foo.cc"};
146 Mangler.adjust(Cmd, "foo.cc");
147 // Directory based on resolved symlink, basename preserved.
148 EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.front());
150 // Set PATH to point to temp/bin so we can find 'foo' on it.
151 ASSERT_TRUE(::getenv("PATH"));
152 auto RestorePath =
153 llvm::make_scope_exit([OldPath = std::string(::getenv("PATH"))] {
154 ::setenv("PATH", OldPath.c_str(), 1);
156 ::setenv("PATH", (TempDir + "/bin").str().c_str(), /*overwrite=*/1);
158 // Test the case where the driver is a $PATH-relative path to a symlink.
159 Mangler = CommandMangler::forTests();
160 Mangler.ClangPath = testPath("fake/clang");
161 // Driver found on PATH.
162 Cmd = {"foo", "foo.cc"};
163 Mangler.adjust(Cmd, "foo.cc");
164 // Found the symlink and resolved the path as above.
165 EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.front());
167 // Symlink not resolved with -no-canonical-prefixes.
168 Cmd = {"foo", "-no-canonical-prefixes", "foo.cc"};
169 Mangler.adjust(Cmd, "foo.cc");
170 EXPECT_EQ((TempDir + "/bin/foo").str(), Cmd.front());
172 #endif
174 TEST(CommandMangler, ConfigEdits) {
175 auto Mangler = CommandMangler::forTests();
176 std::vector<std::string> Cmd = {"clang++", "foo.cc"};
178 Config Cfg;
179 Cfg.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) {
180 for (auto &Arg : Argv)
181 for (char &C : Arg)
182 C = llvm::toUpper(C);
184 Cfg.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) {
185 Argv = tooling::getInsertArgumentAdjuster("--hello")(Argv, "");
187 WithContextValue WithConfig(Config::Key, std::move(Cfg));
188 Mangler.adjust(Cmd, "foo.cc");
190 // Edits are applied in given order and before other mangling and they always
191 // go before filename.
192 EXPECT_THAT(Cmd, ElementsAre(_, "--hello", "--", "FOO.CC"));
195 static std::string strip(llvm::StringRef Arg, llvm::StringRef Argv) {
196 llvm::SmallVector<llvm::StringRef> Parts;
197 llvm::SplitString(Argv, Parts);
198 std::vector<std::string> Args = {Parts.begin(), Parts.end()};
199 ArgStripper S;
200 S.strip(Arg);
201 S.process(Args);
202 return printArgv(Args);
205 TEST(ArgStripperTest, Spellings) {
206 // May use alternate prefixes.
207 EXPECT_EQ(strip("-pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
208 EXPECT_EQ(strip("-pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
209 EXPECT_EQ(strip("--pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
210 EXPECT_EQ(strip("--pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
211 // May use alternate names.
212 EXPECT_EQ(strip("-x", "clang -x c++ foo.cc"), "clang foo.cc");
213 EXPECT_EQ(strip("-x", "clang --language=c++ foo.cc"), "clang foo.cc");
214 EXPECT_EQ(strip("--language=", "clang -x c++ foo.cc"), "clang foo.cc");
215 EXPECT_EQ(strip("--language=", "clang --language=c++ foo.cc"),
216 "clang foo.cc");
219 TEST(ArgStripperTest, UnknownFlag) {
220 EXPECT_EQ(strip("-xyzzy", "clang -xyzzy foo.cc"), "clang foo.cc");
221 EXPECT_EQ(strip("-xyz*", "clang -xyzzy foo.cc"), "clang foo.cc");
222 EXPECT_EQ(strip("-xyzzy", "clang -Xclang -xyzzy foo.cc"), "clang foo.cc");
225 TEST(ArgStripperTest, Xclang) {
226 // Flags may be -Xclang escaped.
227 EXPECT_EQ(strip("-ast-dump", "clang -Xclang -ast-dump foo.cc"),
228 "clang foo.cc");
229 // Args may be -Xclang escaped.
230 EXPECT_EQ(strip("-add-plugin", "clang -Xclang -add-plugin -Xclang z foo.cc"),
231 "clang foo.cc");
234 TEST(ArgStripperTest, ClangCL) {
235 // /I is a synonym for -I in clang-cl mode only.
236 // Not stripped by default.
237 EXPECT_EQ(strip("-I", "clang -I /usr/inc /Interesting/file.cc"),
238 "clang /Interesting/file.cc");
239 // Stripped when invoked as clang-cl.
240 EXPECT_EQ(strip("-I", "clang-cl -I /usr/inc /Interesting/file.cc"),
241 "clang-cl");
242 // Stripped when invoked as CL.EXE
243 EXPECT_EQ(strip("-I", "CL.EXE -I /usr/inc /Interesting/file.cc"), "CL.EXE");
244 // Stripped when passed --driver-mode=cl.
245 EXPECT_EQ(strip("-I", "cc -I /usr/inc /Interesting/file.cc --driver-mode=cl"),
246 "cc --driver-mode=cl");
249 TEST(ArgStripperTest, ArgStyles) {
250 // Flag
251 EXPECT_EQ(strip("-Qn", "clang -Qn foo.cc"), "clang foo.cc");
252 EXPECT_EQ(strip("-Qn", "clang -QnZ foo.cc"), "clang -QnZ foo.cc");
253 // Joined
254 EXPECT_EQ(strip("-std=", "clang -std= foo.cc"), "clang foo.cc");
255 EXPECT_EQ(strip("-std=", "clang -std=c++11 foo.cc"), "clang foo.cc");
256 // Separate
257 EXPECT_EQ(strip("-mllvm", "clang -mllvm X foo.cc"), "clang foo.cc");
258 EXPECT_EQ(strip("-mllvm", "clang -mllvmX foo.cc"), "clang -mllvmX foo.cc");
259 // RemainingArgsJoined
260 EXPECT_EQ(strip("/link", "clang-cl /link b c d foo.cc"), "clang-cl");
261 EXPECT_EQ(strip("/link", "clang-cl /linka b c d foo.cc"), "clang-cl");
262 // CommaJoined
263 EXPECT_EQ(strip("-Wl,", "clang -Wl,x,y foo.cc"), "clang foo.cc");
264 EXPECT_EQ(strip("-Wl,", "clang -Wl, foo.cc"), "clang foo.cc");
265 // MultiArg
266 EXPECT_EQ(strip("-segaddr", "clang -segaddr a b foo.cc"), "clang foo.cc");
267 EXPECT_EQ(strip("-segaddr", "clang -segaddra b foo.cc"),
268 "clang -segaddra b foo.cc");
269 // JoinedOrSeparate
270 EXPECT_EQ(strip("-G", "clang -GX foo.cc"), "clang foo.cc");
271 EXPECT_EQ(strip("-G", "clang -G X foo.cc"), "clang foo.cc");
272 // JoinedAndSeparate
273 EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg-X Y foo.cc"),
274 "clang -cc1 foo.cc");
275 EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg- Y foo.cc"),
276 "clang -cc1 foo.cc");
279 TEST(ArgStripperTest, EndOfList) {
280 // When we hit the end-of-args prematurely, we don't crash.
281 // We consume the incomplete args if we've matched the target option.
282 EXPECT_EQ(strip("-I", "clang -Xclang"), "clang -Xclang");
283 EXPECT_EQ(strip("-I", "clang -Xclang -I"), "clang");
284 EXPECT_EQ(strip("-I", "clang -I -Xclang"), "clang");
285 EXPECT_EQ(strip("-I", "clang -I"), "clang");
288 TEST(ArgStripperTest, Multiple) {
289 ArgStripper S;
290 S.strip("-o");
291 S.strip("-c");
292 std::vector<std::string> Args = {"clang", "-o", "foo.o", "foo.cc", "-c"};
293 S.process(Args);
294 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
297 TEST(ArgStripperTest, Warning) {
299 // -W is a flag name
300 ArgStripper S;
301 S.strip("-W");
302 std::vector<std::string> Args = {"clang", "-Wfoo", "-Wno-bar", "-Werror",
303 "foo.cc"};
304 S.process(Args);
305 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
308 // -Wfoo is not a flag name, matched literally.
309 ArgStripper S;
310 S.strip("-Wunused");
311 std::vector<std::string> Args = {"clang", "-Wunused", "-Wno-unused",
312 "foo.cc"};
313 S.process(Args);
314 EXPECT_THAT(Args, ElementsAre("clang", "-Wno-unused", "foo.cc"));
318 TEST(ArgStripperTest, Define) {
320 // -D is a flag name
321 ArgStripper S;
322 S.strip("-D");
323 std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
324 S.process(Args);
325 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
328 // -Dbar is not: matched literally
329 ArgStripper S;
330 S.strip("-Dbar");
331 std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
332 S.process(Args);
333 EXPECT_THAT(Args, ElementsAre("clang", "-Dfoo", "-Dbar=baz", "foo.cc"));
334 S.strip("-Dfoo");
335 S.process(Args);
336 EXPECT_THAT(Args, ElementsAre("clang", "-Dbar=baz", "foo.cc"));
337 S.strip("-Dbar=*");
338 S.process(Args);
339 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
343 TEST(ArgStripperTest, OrderDependent) {
344 ArgStripper S;
345 // If -include is stripped first, we see -pch as its arg and foo.pch remains.
346 // To get this case right, we must process -include-pch first.
347 S.strip("-include");
348 S.strip("-include-pch");
349 std::vector<std::string> Args = {"clang", "-include-pch", "foo.pch",
350 "foo.cc"};
351 S.process(Args);
352 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
355 TEST(PrintArgvTest, All) {
356 std::vector<llvm::StringRef> Args = {
357 "one", "two", "thr ee", "f\"o\"ur", "fi\\ve", "$"
359 const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
360 EXPECT_EQ(Expected, printArgv(Args));
363 TEST(CommandMangler, InputsAfterDashDash) {
364 const auto Mangler = CommandMangler::forTests();
366 std::vector<std::string> Args = {"clang", "/Users/foo.cc"};
367 Mangler.adjust(Args, "/Users/foo.cc");
368 EXPECT_THAT(llvm::makeArrayRef(Args).take_back(2),
369 ElementsAre("--", "/Users/foo.cc"));
370 EXPECT_THAT(llvm::makeArrayRef(Args).drop_back(2),
371 Not(Contains("/Users/foo.cc")));
373 // In CL mode /U triggers an undef operation, hence `/Users/foo.cc` shouldn't
374 // be interpreted as a file.
376 std::vector<std::string> Args = {"clang", "--driver-mode=cl", "bar.cc",
377 "/Users/foo.cc"};
378 Mangler.adjust(Args, "bar.cc");
379 EXPECT_THAT(llvm::makeArrayRef(Args).take_back(2),
380 ElementsAre("--", "bar.cc"));
381 EXPECT_THAT(llvm::makeArrayRef(Args).drop_back(2), Not(Contains("bar.cc")));
383 // All inputs but the main file is dropped.
385 std::vector<std::string> Args = {"clang", "foo.cc", "bar.cc"};
386 Mangler.adjust(Args, "baz.cc");
387 EXPECT_THAT(llvm::makeArrayRef(Args).take_back(2),
388 ElementsAre("--", "baz.cc"));
389 EXPECT_THAT(
390 llvm::makeArrayRef(Args).drop_back(2),
391 testing::AllOf(Not(Contains("foo.cc")), Not(Contains("bar.cc"))));
395 TEST(CommandMangler, StripsMultipleArch) {
396 const auto Mangler = CommandMangler::forTests();
397 std::vector<std::string> Args = {"clang", "-arch", "foo",
398 "-arch", "bar", "/Users/foo.cc"};
399 Mangler.adjust(Args, "/Users/foo.cc");
400 EXPECT_EQ(
401 llvm::count_if(Args, [](llvm::StringRef Arg) { return Arg == "-arch"; }),
404 // Single arch option is preserved.
405 Args = {"clang", "-arch", "foo", "/Users/foo.cc"};
406 Mangler.adjust(Args, "/Users/foo.cc");
407 EXPECT_EQ(
408 llvm::count_if(Args, [](llvm::StringRef Arg) { return Arg == "-arch"; }),
412 TEST(CommandMangler, EmptyArgs) {
413 const auto Mangler = CommandMangler::forTests();
414 std::vector<std::string> Args = {};
415 // Make sure we don't crash.
416 Mangler.adjust(Args, "foo.cc");
418 } // namespace
419 } // namespace clangd
420 } // namespace clang