brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.7 KiB · 660540a Raw
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