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//===----------------------------------------------------------------------===//
8
9#include "CompileCommands.h"
10#include "Config.h"
11#include "TestFS.h"
12#include "support/Context.h"
13
14#include "clang/Testing/CommandLineArgs.h"
15#include "clang/Tooling/ArgumentsAdjusters.h"
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/ScopeExit.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Support/FileSystem.h"
22#include "llvm/Support/Path.h"
23#include "llvm/Support/Process.h"
24#include "llvm/Support/TargetSelect.h"
25
26#include "gmock/gmock.h"
27#include "gtest/gtest.h"
28
29namespace clang {
30namespace clangd {
31namespace {
32
33using ::testing::_;
34using ::testing::Contains;
35using ::testing::ElementsAre;
36using ::testing::HasSubstr;
37using ::testing::Not;
38
39// Sadly, CommandMangler::detect(), which contains much of the logic, is
40// a bunch of untested integration glue. We test the string manipulation here
41// assuming its results are correct.
42
43// Make use of all features and assert the exact command we get out.
44// Other tests just verify presence/absence of certain args.
45TEST(CommandMangler, Everything) {
46 llvm::InitializeAllTargetInfos(); // As in ClangdMain
47 std::string Target = getAnyTargetForTesting();
48 auto Mangler = CommandMangler::forTests();
49 Mangler.ClangPath = testPath(File: "fake/clang");
50 Mangler.ResourceDir = testPath(File: "fake/resources");
51 Mangler.Sysroot = testPath(File: "fake/sysroot");
52 tooling::CompileCommand Cmd;
53 Cmd.CommandLine = {Target + "-clang++", "--", "foo.cc", "bar.cc"};
54 Mangler(Cmd, "foo.cc");
55 EXPECT_THAT(Cmd.CommandLine,
56 ElementsAre(testPath("fake/" + Target + "-clang++"),
57 "--target=" + Target, "--driver-mode=g++",
58 "-resource-dir=" + testPath("fake/resources"),
59 "-isysroot", testPath("fake/sysroot"), "--",
60 "foo.cc"));
61}
62
63TEST(CommandMangler, FilenameMismatch) {
64 auto Mangler = CommandMangler::forTests();
65 Mangler.ClangPath = testPath(File: "clang");
66 // Our compile flags refer to foo.cc...
67 tooling::CompileCommand Cmd;
68 Cmd.CommandLine = {"clang", "foo.cc"};
69 // but we're applying it to foo.h...
70 Mangler(Cmd, "foo.h");
71 // so transferCompileCommand should add -x c++-header to preserve semantics.
72 EXPECT_THAT(Cmd.CommandLine, ElementsAre(testPath("clang"), "-x",
73 "c++-header", "--", "foo.h"));
74}
75
76TEST(CommandMangler, ResourceDir) {
77 auto Mangler = CommandMangler::forTests();
78 Mangler.ResourceDir = testPath(File: "fake/resources");
79 tooling::CompileCommand Cmd;
80 Cmd.CommandLine = {"clang++", "foo.cc"};
81 Mangler(Cmd, "foo.cc");
82 EXPECT_THAT(Cmd.CommandLine,
83 Contains("-resource-dir=" + testPath("fake/resources")));
84}
85
86TEST(CommandMangler, Sysroot) {
87 auto Mangler = CommandMangler::forTests();
88 Mangler.Sysroot = testPath(File: "fake/sysroot");
89
90 tooling::CompileCommand Cmd;
91 Cmd.CommandLine = {"clang++", "foo.cc"};
92 Mangler(Cmd, "foo.cc");
93 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
94 HasSubstr("-isysroot " + testPath("fake/sysroot")));
95}
96
97TEST(CommandMangler, ClangPath) {
98 auto Mangler = CommandMangler::forTests();
99 Mangler.ClangPath = testPath(File: "fake/clang");
100
101 tooling::CompileCommand Cmd;
102 Cmd.CommandLine = {"clang++", "foo.cc"};
103 Mangler(Cmd, "foo.cc");
104 EXPECT_EQ(testPath("fake/clang++"), Cmd.CommandLine.front());
105
106 Cmd.CommandLine = {"unknown-binary", "foo.cc"};
107 Mangler(Cmd, "foo.cc");
108 EXPECT_EQ(testPath("fake/unknown-binary"), Cmd.CommandLine.front());
109
110 Cmd.CommandLine = {testPath(File: "path/clang++"), "foo.cc"};
111 Mangler(Cmd, "foo.cc");
112 EXPECT_EQ(testPath("path/clang++"), Cmd.CommandLine.front());
113
114 Cmd.CommandLine = {"foo/unknown-binary", "foo.cc"};
115 Mangler(Cmd, "foo.cc");
116 EXPECT_EQ("foo/unknown-binary", Cmd.CommandLine.front());
117}
118
119// Only run the PATH/symlink resolving test on unix, we need to fiddle
120// with permissions and environment variables...
121#ifdef LLVM_ON_UNIX
122MATCHER(ok, "") {
123 if (arg) {
124 *result_listener << arg.message();
125 return false;
126 }
127 return true;
128}
129
130TEST(CommandMangler, ClangPathResolve) {
131 // Set up filesystem:
132 // /temp/
133 // bin/
134 // foo -> temp/lib/bar
135 // lib/
136 // bar
137 llvm::SmallString<256> TempDir;
138 ASSERT_THAT(llvm::sys::fs::createUniqueDirectory("ClangPathResolve", TempDir),
139 ok());
140 // /var/tmp is a symlink on Mac. Resolve it so we're asserting the right path.
141 ASSERT_THAT(llvm::sys::fs::real_path(TempDir.str(), TempDir), ok());
142 auto CleanDir = llvm::make_scope_exit(
143 F: [&] { llvm::sys::fs::remove_directories(path: TempDir); });
144 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/bin"), ok());
145 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/lib"), ok());
146 int FD;
147 ASSERT_THAT(llvm::sys::fs::openFileForWrite(TempDir + "/lib/bar", FD), ok());
148 ASSERT_THAT(llvm::sys::Process::SafelyCloseFileDescriptor(FD), ok());
149 ::chmod(file: (TempDir + "/lib/bar").str().c_str(), mode: 0755); // executable
150 ASSERT_THAT(
151 llvm::sys::fs::create_link(TempDir + "/lib/bar", TempDir + "/bin/foo"),
152 ok());
153
154 // Test the case where the driver is an absolute path to a symlink.
155 auto Mangler = CommandMangler::forTests();
156 Mangler.ClangPath = testPath(File: "fake/clang");
157 tooling::CompileCommand Cmd;
158 Cmd.CommandLine = {(TempDir + "/bin/foo").str(), "foo.cc"};
159 Mangler(Cmd, "foo.cc");
160 // Directory based on resolved symlink, basename preserved.
161 EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.CommandLine.front());
162
163 // Set PATH to point to temp/bin so we can find 'foo' on it.
164 ASSERT_TRUE(::getenv("PATH"));
165 auto RestorePath =
166 llvm::make_scope_exit(F: [OldPath = std::string(::getenv(name: "PATH"))] {
167 ::setenv(name: "PATH", value: OldPath.c_str(), replace: 1);
168 });
169 ::setenv(name: "PATH", value: (TempDir + "/bin").str().c_str(), /*overwrite=*/replace: 1);
170
171 // Test the case where the driver is a $PATH-relative path to a symlink.
172 Mangler = CommandMangler::forTests();
173 Mangler.ClangPath = testPath(File: "fake/clang");
174 // Driver found on PATH.
175 Cmd.CommandLine = {"foo", "foo.cc"};
176 Mangler(Cmd, "foo.cc");
177 // Found the symlink and resolved the path as above.
178 EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.CommandLine.front());
179
180 // Symlink not resolved with -no-canonical-prefixes.
181 Cmd.CommandLine = {"foo", "-no-canonical-prefixes", "foo.cc"};
182 Mangler(Cmd, "foo.cc");
183 EXPECT_EQ((TempDir + "/bin/foo").str(), Cmd.CommandLine.front());
184}
185#endif
186
187TEST(CommandMangler, ConfigEdits) {
188 auto Mangler = CommandMangler::forTests();
189 tooling::CompileCommand Cmd;
190 Cmd.CommandLine = {"clang++", "foo.cc"};
191 {
192 Config Cfg;
193 Cfg.CompileFlags.Edits.push_back(x: [](std::vector<std::string> &Argv) {
194 for (auto &Arg : Argv)
195 for (char &C : Arg)
196 C = llvm::toUpper(x: C);
197 });
198 Cfg.CompileFlags.Edits.push_back(x: [](std::vector<std::string> &Argv) {
199 Argv = tooling::getInsertArgumentAdjuster(Extra: "--hello")(Argv, "");
200 });
201 WithContextValue WithConfig(Config::Key, std::move(Cfg));
202 Mangler(Cmd, "foo.cc");
203 }
204 // Edits are applied in given order and before other mangling and they always
205 // go before filename. `--driver-mode=g++` here is in lower case because
206 // options inserted by addTargetAndModeForProgramName are not editable,
207 // see discussion in https://reviews.llvm.org/D138546
208 EXPECT_THAT(Cmd.CommandLine,
209 ElementsAre(_, "--driver-mode=g++", "--hello", "--", "FOO.CC"));
210}
211
212static std::string strip(llvm::StringRef Arg, llvm::StringRef Argv) {
213 llvm::SmallVector<llvm::StringRef> Parts;
214 llvm::SplitString(Source: Argv, OutFragments&: Parts);
215 std::vector<std::string> Args = {Parts.begin(), Parts.end()};
216 ArgStripper S;
217 S.strip(Arg);
218 S.process(Args);
219 return printArgv(Args);
220}
221
222TEST(ArgStripperTest, Spellings) {
223 // May use alternate prefixes.
224 EXPECT_EQ(strip("-pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
225 EXPECT_EQ(strip("-pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
226 EXPECT_EQ(strip("--pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
227 EXPECT_EQ(strip("--pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
228 // May use alternate names.
229 EXPECT_EQ(strip("-x", "clang -x c++ foo.cc"), "clang foo.cc");
230 EXPECT_EQ(strip("-x", "clang --language=c++ foo.cc"), "clang foo.cc");
231 EXPECT_EQ(strip("--language=", "clang -x c++ foo.cc"), "clang foo.cc");
232 EXPECT_EQ(strip("--language=", "clang --language=c++ foo.cc"),
233 "clang foo.cc");
234}
235
236TEST(ArgStripperTest, UnknownFlag) {
237 EXPECT_EQ(strip("-xyzzy", "clang -xyzzy foo.cc"), "clang foo.cc");
238 EXPECT_EQ(strip("-xyz*", "clang -xyzzy foo.cc"), "clang foo.cc");
239 EXPECT_EQ(strip("-xyzzy", "clang -Xclang -xyzzy foo.cc"), "clang foo.cc");
240}
241
242TEST(ArgStripperTest, Xclang) {
243 // Flags may be -Xclang escaped.
244 EXPECT_EQ(strip("-ast-dump", "clang -Xclang -ast-dump foo.cc"),
245 "clang foo.cc");
246 // Args may be -Xclang escaped.
247 EXPECT_EQ(strip("-add-plugin", "clang -Xclang -add-plugin -Xclang z foo.cc"),
248 "clang foo.cc");
249}
250
251TEST(ArgStripperTest, ClangCL) {
252 // /I is a synonym for -I in clang-cl mode only.
253 // Not stripped by default.
254 EXPECT_EQ(strip("-I", "clang -I /usr/inc /Interesting/file.cc"),
255 "clang /Interesting/file.cc");
256 // Stripped when invoked as clang-cl.
257 EXPECT_EQ(strip("-I", "clang-cl -I /usr/inc /Interesting/file.cc"),
258 "clang-cl");
259 // Stripped when invoked as CL.EXE
260 EXPECT_EQ(strip("-I", "CL.EXE -I /usr/inc /Interesting/file.cc"), "CL.EXE");
261 // Stripped when passed --driver-mode=cl.
262 EXPECT_EQ(strip("-I", "cc -I /usr/inc /Interesting/file.cc --driver-mode=cl"),
263 "cc --driver-mode=cl");
264}
265
266TEST(ArgStripperTest, ArgStyles) {
267 // Flag
268 EXPECT_EQ(strip("-Qn", "clang -Qn foo.cc"), "clang foo.cc");
269 EXPECT_EQ(strip("-Qn", "clang -QnZ foo.cc"), "clang -QnZ foo.cc");
270 // Joined
271 EXPECT_EQ(strip("-std=", "clang -std= foo.cc"), "clang foo.cc");
272 EXPECT_EQ(strip("-std=", "clang -std=c++11 foo.cc"), "clang foo.cc");
273 // Separate
274 EXPECT_EQ(strip("-mllvm", "clang -mllvm X foo.cc"), "clang foo.cc");
275 EXPECT_EQ(strip("-mllvm", "clang -mllvmX foo.cc"), "clang -mllvmX foo.cc");
276 // RemainingArgsJoined
277 EXPECT_EQ(strip("/link", "clang-cl /link b c d foo.cc"), "clang-cl");
278 EXPECT_EQ(strip("/link", "clang-cl /linka b c d foo.cc"), "clang-cl");
279 // CommaJoined
280 EXPECT_EQ(strip("-Wl,", "clang -Wl,x,y foo.cc"), "clang foo.cc");
281 EXPECT_EQ(strip("-Wl,", "clang -Wl, foo.cc"), "clang foo.cc");
282 // MultiArg
283 EXPECT_EQ(strip("-segaddr", "clang -segaddr a b foo.cc"), "clang foo.cc");
284 EXPECT_EQ(strip("-segaddr", "clang -segaddra b foo.cc"),
285 "clang -segaddra b foo.cc");
286 // JoinedOrSeparate
287 EXPECT_EQ(strip("-G", "clang -GX foo.cc"), "clang foo.cc");
288 EXPECT_EQ(strip("-G", "clang -G X foo.cc"), "clang foo.cc");
289 // JoinedAndSeparate
290 EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg-X Y foo.cc"),
291 "clang -cc1 foo.cc");
292 EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg- Y foo.cc"),
293 "clang -cc1 foo.cc");
294}
295
296TEST(ArgStripperTest, EndOfList) {
297 // When we hit the end-of-args prematurely, we don't crash.
298 // We consume the incomplete args if we've matched the target option.
299 EXPECT_EQ(strip("-I", "clang -Xclang"), "clang -Xclang");
300 EXPECT_EQ(strip("-I", "clang -Xclang -I"), "clang");
301 EXPECT_EQ(strip("-I", "clang -I -Xclang"), "clang");
302 EXPECT_EQ(strip("-I", "clang -I"), "clang");
303}
304
305TEST(ArgStripperTest, Multiple) {
306 ArgStripper S;
307 S.strip(Arg: "-o");
308 S.strip(Arg: "-c");
309 std::vector<std::string> Args = {"clang", "-o", "foo.o", "foo.cc", "-c"};
310 S.process(Args);
311 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
312}
313
314TEST(ArgStripperTest, Warning) {
315 {
316 // -W is a flag name
317 ArgStripper S;
318 S.strip(Arg: "-W");
319 std::vector<std::string> Args = {"clang", "-Wfoo", "-Wno-bar", "-Werror",
320 "foo.cc"};
321 S.process(Args);
322 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
323 }
324 {
325 // -Wfoo is not a flag name, matched literally.
326 ArgStripper S;
327 S.strip(Arg: "-Wunused");
328 std::vector<std::string> Args = {"clang", "-Wunused", "-Wno-unused",
329 "foo.cc"};
330 S.process(Args);
331 EXPECT_THAT(Args, ElementsAre("clang", "-Wno-unused", "foo.cc"));
332 }
333}
334
335TEST(ArgStripperTest, Define) {
336 {
337 // -D is a flag name
338 ArgStripper S;
339 S.strip(Arg: "-D");
340 std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
341 S.process(Args);
342 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
343 }
344 {
345 // -Dbar is not: matched literally
346 ArgStripper S;
347 S.strip(Arg: "-Dbar");
348 std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
349 S.process(Args);
350 EXPECT_THAT(Args, ElementsAre("clang", "-Dfoo", "-Dbar=baz", "foo.cc"));
351 S.strip(Arg: "-Dfoo");
352 S.process(Args);
353 EXPECT_THAT(Args, ElementsAre("clang", "-Dbar=baz", "foo.cc"));
354 S.strip(Arg: "-Dbar=*");
355 S.process(Args);
356 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
357 }
358}
359
360TEST(ArgStripperTest, OrderDependent) {
361 ArgStripper S;
362 // If -include is stripped first, we see -pch as its arg and foo.pch remains.
363 // To get this case right, we must process -include-pch first.
364 S.strip(Arg: "-include");
365 S.strip(Arg: "-include-pch");
366 std::vector<std::string> Args = {"clang", "-include-pch", "foo.pch",
367 "foo.cc"};
368 S.process(Args);
369 EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
370}
371
372TEST(PrintArgvTest, All) {
373 std::vector<llvm::StringRef> Args = {"one", "two", "thr ee",
374 "f\"o\"ur", "fi\\ve", "$"};
375 const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
376 EXPECT_EQ(Expected, printArgv(Args));
377}
378
379TEST(CommandMangler, InputsAfterDashDash) {
380 const auto Mangler = CommandMangler::forTests();
381 {
382 tooling::CompileCommand Cmd;
383 Cmd.CommandLine = {"clang", "/Users/foo.cc"};
384 Mangler(Cmd, "/Users/foo.cc");
385 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
386 ElementsAre("--", "/Users/foo.cc"));
387 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
388 Not(Contains("/Users/foo.cc")));
389 }
390 // In CL mode /U triggers an undef operation, hence `/Users/foo.cc` shouldn't
391 // be interpreted as a file.
392 {
393 tooling::CompileCommand Cmd;
394 Cmd.CommandLine = {"clang", "--driver-mode=cl", "bar.cc", "/Users/foo.cc"};
395 Mangler(Cmd, "bar.cc");
396 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
397 ElementsAre("--", "bar.cc"));
398 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
399 Not(Contains("bar.cc")));
400 }
401 // All inputs but the main file is dropped.
402 {
403 tooling::CompileCommand Cmd;
404 Cmd.CommandLine = {"clang", "foo.cc", "bar.cc"};
405 Mangler(Cmd, "baz.cc");
406 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
407 ElementsAre("--", "baz.cc"));
408 EXPECT_THAT(
409 llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
410 testing::AllOf(Not(Contains("foo.cc")), Not(Contains("bar.cc"))));
411 }
412}
413
414TEST(CommandMangler, StripsMultipleArch) {
415 const auto Mangler = CommandMangler::forTests();
416 tooling::CompileCommand Cmd;
417 Cmd.CommandLine = {"clang", "-arch", "foo", "-arch", "bar", "/Users/foo.cc"};
418 Mangler(Cmd, "/Users/foo.cc");
419 EXPECT_EQ(llvm::count_if(Cmd.CommandLine,
420 [](llvm::StringRef Arg) { return Arg == "-arch"; }),
421 0);
422
423 // Single arch option is preserved.
424 Cmd.CommandLine = {"clang", "-arch", "foo", "/Users/foo.cc"};
425 Mangler(Cmd, "/Users/foo.cc");
426 EXPECT_EQ(llvm::count_if(Cmd.CommandLine,
427 [](llvm::StringRef Arg) { return Arg == "-arch"; }),
428 1);
429}
430
431TEST(CommandMangler, EmptyArgs) {
432 const auto Mangler = CommandMangler::forTests();
433 tooling::CompileCommand Cmd;
434 Cmd.CommandLine = {};
435 // Make sure we don't crash.
436 Mangler(Cmd, "foo.cc");
437}
438
439TEST(CommandMangler, PathsAsPositional) {
440 const auto Mangler = CommandMangler::forTests();
441 tooling::CompileCommand Cmd;
442 Cmd.CommandLine = {
443 "clang",
444 "--driver-mode=cl",
445 "-I",
446 "foo",
447 };
448 // Make sure we don't crash.
449 Mangler(Cmd, "a.cc");
450 EXPECT_THAT(Cmd.CommandLine, Contains("foo"));
451}
452
453TEST(CommandMangler, RespectsOriginalResourceDir) {
454 auto Mangler = CommandMangler::forTests();
455 Mangler.ResourceDir = testPath(File: "fake/resources");
456
457 {
458 tooling::CompileCommand Cmd;
459 Cmd.CommandLine = {"clang++", "-resource-dir", testPath(File: "true/resources"),
460 "foo.cc"};
461 Mangler(Cmd, "foo.cc");
462 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
463 HasSubstr("-resource-dir " + testPath("true/resources")));
464 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
465 Not(HasSubstr(testPath("fake/resources"))));
466 }
467
468 {
469 tooling::CompileCommand Cmd;
470 Cmd.CommandLine = {"clang++", "-resource-dir=" + testPath(File: "true/resources"),
471 "foo.cc"};
472 Mangler(Cmd, "foo.cc");
473 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
474 HasSubstr("-resource-dir=" + testPath("true/resources")));
475 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
476 Not(HasSubstr(testPath("fake/resources"))));
477 }
478}
479
480TEST(CommandMangler, RespectsOriginalSysroot) {
481 auto Mangler = CommandMangler::forTests();
482 Mangler.Sysroot = testPath(File: "fake/sysroot");
483
484 {
485 tooling::CompileCommand Cmd;
486 Cmd.CommandLine = {"clang++", "-isysroot", testPath(File: "true/sysroot"),
487 "foo.cc"};
488 Mangler(Cmd, "foo.cc");
489 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
490 HasSubstr("-isysroot " + testPath("true/sysroot")));
491 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
492 Not(HasSubstr(testPath("fake/sysroot"))));
493 }
494
495 {
496 tooling::CompileCommand Cmd;
497 Cmd.CommandLine = {"clang++", "-isysroot" + testPath(File: "true/sysroot"),
498 "foo.cc"};
499 Mangler(Cmd, "foo.cc");
500 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
501 HasSubstr("-isysroot" + testPath("true/sysroot")));
502 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
503 Not(HasSubstr(testPath("fake/sysroot"))));
504 }
505
506 {
507 tooling::CompileCommand Cmd;
508 Cmd.CommandLine = {"clang++", "--sysroot", testPath(File: "true/sysroot"),
509 "foo.cc"};
510 Mangler(Cmd, "foo.cc");
511 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
512 HasSubstr("--sysroot " + testPath("true/sysroot")));
513 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
514 Not(HasSubstr(testPath("fake/sysroot"))));
515 }
516
517 {
518 tooling::CompileCommand Cmd;
519 Cmd.CommandLine = {"clang++", "--sysroot=" + testPath(File: "true/sysroot"),
520 "foo.cc"};
521 Mangler(Cmd, "foo.cc");
522 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
523 HasSubstr("--sysroot=" + testPath("true/sysroot")));
524 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
525 Not(HasSubstr(testPath("fake/sysroot"))));
526 }
527}
528} // namespace
529} // namespace clangd
530} // namespace clang
531

source code of clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp