551 lines · cpp
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-exception6//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_UNIX22#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 30namespace clang {31namespace clangd {32namespace {33 34using ::testing::_;35using ::testing::Contains;36using ::testing::ElementsAre;37using ::testing::HasSubstr;38using ::testing::Not;39 40// Sadly, CommandMangler::detect(), which contains much of the logic, is41// a bunch of untested integration glue. We test the string manipulation here42// 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.46TEST(CommandMangler, Everything) {47 llvm::InitializeAllTargetInfos(); // As in ClangdMain48 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 64TEST(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 77TEST(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 87TEST(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 98TEST(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 fiddle121// with permissions and environment variables...122#ifdef LLVM_ON_UNIX123MATCHER(ok, "") {124 if (arg) {125 *result_listener << arg.message();126 return false;127 }128 return true;129}130 131TEST(CommandMangler, ClangPathResolve) {132 // Set up filesystem:133 // /temp/134 // bin/135 // foo -> temp/lib/bar136 // lib/137 // bar138 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); // executable151 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#endif187 188TEST(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 always206 // go before filename. `--driver-mode=g++` here is in lower case because207 // options inserted by addTargetAndModeForProgramName are not editable,208 // see discussion in https://reviews.llvm.org/D138546209 EXPECT_THAT(Cmd.CommandLine,210 ElementsAre(_, "--driver-mode=g++", "--hello", "--", "FOO.CC"));211}212 213static 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 223TEST(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 237TEST(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 243TEST(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 252TEST(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.EXE261 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 267TEST(ArgStripperTest, ArgStyles) {268 // Flag269 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 // Joined272 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 // Separate275 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 // RemainingArgsJoined278 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 // CommaJoined281 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 // MultiArg284 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 // JoinedOrSeparate288 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 // JoinedAndSeparate291 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 297TEST(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 306TEST(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 315TEST(ArgStripperTest, Warning) {316 {317 // -W is a flag name318 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 336TEST(ArgStripperTest, Define) {337 {338 // -D is a flag name339 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 literally347 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 361TEST(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 373TEST(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 380TEST(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't392 // 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 415TEST(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 432TEST(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 440TEST(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 454TEST(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 481TEST(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 530TEST(CommandMangler, StdLatestFlag) {531 const auto Mangler = CommandMangler::forTests();532 tooling::CompileCommand Cmd;533 Cmd.CommandLine = {"clang-cl", "/std:c++latest", "--", "/Users/foo.cc"};534 Mangler(Cmd, "/Users/foo.cc");535 // Check that the /std:c++latest flag is not dropped536 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++latest"));537}538 539TEST(CommandMangler, StdLatestFlag_Inference) {540 const auto Mangler = CommandMangler::forTests();541 tooling::CompileCommand Cmd;542 Cmd.CommandLine = {"clang-cl", "/std:c++latest", "--", "/Users/foo.cc"};543 Mangler(Cmd, "/Users/foo.hpp");544 // Check that the /std:c++latest flag is not dropped during inference545 EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++latest"));546}547 548} // namespace549} // namespace clangd550} // namespace clang551