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