brintos

brintos / llvm-project-archived public Read only

0
0
Text · 82.5 KiB · 7f538f1 Raw
2352 lines · cpp
1//===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine tests ------===//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 "llvm/Support/CommandLine.h"10#include "llvm/ADT/STLExtras.h"11#include "llvm/ADT/SmallString.h"12#include "llvm/ADT/StringRef.h"13#include "llvm/Config/config.h"14#include "llvm/Support/Allocator.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/InitLLVM.h"17#include "llvm/Support/MemoryBuffer.h"18#include "llvm/Support/Path.h"19#include "llvm/Support/Program.h"20#include "llvm/Support/StringSaver.h"21#include "llvm/Support/VirtualFileSystem.h"22#include "llvm/Support/raw_ostream.h"23#include "llvm/TargetParser/Host.h"24#include "llvm/TargetParser/Triple.h"25#include "llvm/Testing/Support/SupportHelpers.h"26#include "gmock/gmock.h"27#include "gtest/gtest.h"28#include <fstream>29#include <stdlib.h>30#include <string>31#if HAVE_UNISTD_H32#include <unistd.h>33#endif34 35using namespace llvm;36using llvm::unittest::TempDir;37using llvm::unittest::TempFile;38 39namespace {40 41MATCHER(StringEquality, "Checks if two char* are equal as strings") {42  return std::string(std::get<0>(arg)) == std::string(std::get<1>(arg));43}44 45class TempEnvVar {46 public:47  TempEnvVar(const char *name, const char *value)48      : name(name) {49    const char *old_value = getenv(name);50    EXPECT_EQ(nullptr, old_value) << old_value;51#if HAVE_SETENV52    setenv(name, value, true);53#endif54  }55 56  ~TempEnvVar() {57#if HAVE_SETENV58    // Assume setenv and unsetenv come together.59    unsetenv(name);60#else61    (void)name; // Suppress -Wunused-private-field.62#endif63  }64 65 private:66  const char *const name;67};68 69template <typename T, typename Base = cl::opt<T>>70class StackOption : public Base {71public:72  template <class... Ts>73  explicit StackOption(Ts &&... Ms) : Base(std::forward<Ts>(Ms)...) {}74 75  ~StackOption() override { this->removeArgument(); }76 77  template <class DT> StackOption<T> &operator=(const DT &V) {78    Base::operator=(V);79    return *this;80  }81};82 83class StackSubCommand : public cl::SubCommand {84public:85  StackSubCommand(StringRef Name,86                  StringRef Description = StringRef())87      : SubCommand(Name, Description) {}88 89  StackSubCommand() : SubCommand() {}90 91  ~StackSubCommand() { unregisterSubCommand(); }92};93 94 95cl::OptionCategory TestCategory("Test Options", "Description");96TEST(CommandLineTest, ModifyExisitingOption) {97  StackOption<int> TestOption("test-option", cl::desc("old description"));98 99  static const char Description[] = "New description";100  static const char ArgString[] = "new-test-option";101  static const char ValueString[] = "Integer";102 103  StringMap<cl::Option *> &Map =104      cl::getRegisteredOptions(cl::SubCommand::getTopLevel());105 106  ASSERT_EQ(Map.count("test-option"), 1u) << "Could not find option in map.";107 108  cl::Option *Retrieved = Map["test-option"];109  ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";110 111  ASSERT_NE(Retrieved->Categories.end(),112            find_if(Retrieved->Categories,113                    [&](const llvm::cl::OptionCategory *Cat) {114                      return Cat == &cl::getGeneralCategory();115                    }))116      << "Incorrect default option category.";117 118  Retrieved->addCategory(TestCategory);119  ASSERT_NE(Retrieved->Categories.end(),120            find_if(Retrieved->Categories,121                    [&](const llvm::cl::OptionCategory *Cat) {122                      return Cat == &TestCategory;123                    }))124      << "Failed to modify option's option category.";125 126  Retrieved->setDescription(Description);127  ASSERT_STREQ(Retrieved->HelpStr.data(), Description)128      << "Changing option description failed.";129 130  Retrieved->setArgStr(ArgString);131  ASSERT_STREQ(ArgString, Retrieved->ArgStr.data())132      << "Failed to modify option's Argument string.";133 134  Retrieved->setValueStr(ValueString);135  ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString)136      << "Failed to modify option's Value string.";137 138  Retrieved->setHiddenFlag(cl::Hidden);139  ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<140    "Failed to modify option's hidden flag.";141}142 143TEST(CommandLineTest, UseOptionCategory) {144  StackOption<int> TestOption2("test-option", cl::cat(TestCategory));145 146  ASSERT_NE(TestOption2.Categories.end(),147            find_if(TestOption2.Categories,148                         [&](const llvm::cl::OptionCategory *Cat) {149                           return Cat == &TestCategory;150                         }))151      << "Failed to assign Option Category.";152}153 154TEST(CommandLineTest, UseMultipleCategories) {155  StackOption<int> TestOption2("test-option2", cl::cat(TestCategory),156                               cl::cat(cl::getGeneralCategory()),157                               cl::cat(cl::getGeneralCategory()));158 159  // Make sure cl::getGeneralCategory() wasn't added twice.160  ASSERT_EQ(TestOption2.Categories.size(), 2U);161 162  ASSERT_NE(TestOption2.Categories.end(),163            find_if(TestOption2.Categories,164                         [&](const llvm::cl::OptionCategory *Cat) {165                           return Cat == &TestCategory;166                         }))167      << "Failed to assign Option Category.";168  ASSERT_NE(TestOption2.Categories.end(),169            find_if(TestOption2.Categories,170                    [&](const llvm::cl::OptionCategory *Cat) {171                      return Cat == &cl::getGeneralCategory();172                    }))173      << "Failed to assign General Category.";174 175  cl::OptionCategory AnotherCategory("Additional test Options", "Description");176  StackOption<int> TestOption("test-option", cl::cat(TestCategory),177                              cl::cat(AnotherCategory));178  ASSERT_EQ(TestOption.Categories.end(),179            find_if(TestOption.Categories,180                    [&](const llvm::cl::OptionCategory *Cat) {181                      return Cat == &cl::getGeneralCategory();182                    }))183      << "Failed to remove General Category.";184  ASSERT_NE(TestOption.Categories.end(),185            find_if(TestOption.Categories,186                         [&](const llvm::cl::OptionCategory *Cat) {187                           return Cat == &TestCategory;188                         }))189      << "Failed to assign Option Category.";190  ASSERT_NE(TestOption.Categories.end(),191            find_if(TestOption.Categories,192                         [&](const llvm::cl::OptionCategory *Cat) {193                           return Cat == &AnotherCategory;194                         }))195      << "Failed to assign Another Category.";196}197 198typedef void ParserFunction(StringRef Source, StringSaver &Saver,199                            SmallVectorImpl<const char *> &NewArgv,200                            bool MarkEOLs);201 202void testCommandLineTokenizer(ParserFunction *parse, StringRef Input,203                              ArrayRef<const char *> Output,204                              bool MarkEOLs = false) {205  SmallVector<const char *, 0> Actual;206  BumpPtrAllocator A;207  StringSaver Saver(A);208  parse(Input, Saver, Actual, MarkEOLs);209  EXPECT_EQ(Output.size(), Actual.size());210  for (unsigned I = 0, E = Actual.size(); I != E; ++I) {211    if (I < Output.size()) {212      EXPECT_STREQ(Output[I], Actual[I]);213    }214  }215}216 217TEST(CommandLineTest, TokenizeGNUCommandLine) {218  const char Input[] =219      "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) "220      "foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\"";221  const char *const Output[] = {222      "foo bar",     "foo bar",   "foo bar",          "foo\\bar",223      "-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"};224  testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output);225}226 227TEST(CommandLineTest, TokenizeWindowsCommandLine1) {228  const char Input[] =229      R"(a\b c\\d e\\"f g" h\"i j\\\"k "lmn" o pqr "st \"u" \v)";230  const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",231                                 "lmn", "o", "pqr", "st \"u", "\\v" };232  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output);233}234 235TEST(CommandLineTest, TokenizeWindowsCommandLine2) {236  const char Input[] = "clang -c -DFOO=\"\"\"ABC\"\"\" x.cpp";237  const char *const Output[] = { "clang", "-c", "-DFOO=\"ABC\"", "x.cpp"};238  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output);239}240 241TEST(CommandLineTest, TokenizeWindowsCommandLineQuotedLastArgument) {242  // Whitespace at the end of the command line doesn't cause an empty last word243  const char Input0[] = R"(a b c d )";244  const char *const Output0[] = {"a", "b", "c", "d"};245  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input0, Output0);246 247  // But an explicit "" does248  const char Input1[] = R"(a b c d "")";249  const char *const Output1[] = {"a", "b", "c", "d", ""};250  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input1, Output1);251 252  // An unterminated quoted string is also emitted as an argument word, empty253  // or not254  const char Input2[] = R"(a b c d ")";255  const char *const Output2[] = {"a", "b", "c", "d", ""};256  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input2, Output2);257  const char Input3[] = R"(a b c d "text)";258  const char *const Output3[] = {"a", "b", "c", "d", "text"};259  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input3, Output3);260}261 262TEST(CommandLineTest, TokenizeWindowsCommandLineExeName) {263  const char Input1[] =264      R"("C:\Program Files\Whatever\"clang.exe z.c -DY=\"x\")";265  const char *const Output1[] = {"C:\\Program Files\\Whatever\\clang.exe",266                                 "z.c", "-DY=\"x\""};267  testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input1, Output1);268 269  const char Input2[] = "\"a\\\"b c\\\"d\n\"e\\\"f g\\\"h\n";270  const char *const Output2[] = {"a\\b", "c\"d", nullptr,271                                 "e\\f", "g\"h", nullptr};272  testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input2, Output2,273                           /*MarkEOLs=*/true);274 275  const char Input3[] = R"(\\server\share\subdir\clang.exe)";276  const char *const Output3[] = {"\\\\server\\share\\subdir\\clang.exe"};277  testCommandLineTokenizer(cl::TokenizeWindowsCommandLineFull, Input3, Output3);278}279 280TEST(CommandLineTest, TokenizeAndMarkEOLs) {281  // Clang uses EOL marking in response files to support options that consume282  // the rest of the arguments on the current line, but do not consume arguments283  // from subsequent lines. For example, given these rsp files contents:284  // /c /Zi /O2285  // /Oy- /link /debug /opt:ref286  // /Zc:ThreadsafeStatics-287  //288  // clang-cl needs to treat "/debug /opt:ref" as linker flags, and everything289  // else as compiler flags. The tokenizer inserts nullptr sentinels into the290  // output so that clang-cl can find the end of the current line.291  const char Input[] = "clang -Xclang foo\n\nfoo\"bar\"baz\n x.cpp\n";292  const char *const Output[] = {"clang", "-Xclang", "foo",293                                nullptr, nullptr,   "foobarbaz",294                                nullptr, "x.cpp",   nullptr};295  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,296                           /*MarkEOLs=*/true);297  testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,298                           /*MarkEOLs=*/true);299}300 301TEST(CommandLineTest, TokenizeConfigFile1) {302  const char *Input = "\\";303  const char *const Output[] = { "\\" };304  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);305}306 307TEST(CommandLineTest, TokenizeConfigFile2) {308  const char *Input = "\\abc";309  const char *const Output[] = { "abc" };310  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);311}312 313TEST(CommandLineTest, TokenizeConfigFile3) {314  const char *Input = "abc\\";315  const char *const Output[] = { "abc\\" };316  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);317}318 319TEST(CommandLineTest, TokenizeConfigFile4) {320  const char *Input = "abc\\\n123";321  const char *const Output[] = { "abc123" };322  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);323}324 325TEST(CommandLineTest, TokenizeConfigFile5) {326  const char *Input = "abc\\\r\n123";327  const char *const Output[] = { "abc123" };328  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);329}330 331TEST(CommandLineTest, TokenizeConfigFile6) {332  const char *Input = "abc\\\n";333  const char *const Output[] = { "abc" };334  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);335}336 337TEST(CommandLineTest, TokenizeConfigFile7) {338  const char *Input = "abc\\\r\n";339  const char *const Output[] = { "abc" };340  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);341}342 343TEST(CommandLineTest, TokenizeConfigFile8) {344  SmallVector<const char *, 0> Actual;345  BumpPtrAllocator A;346  StringSaver Saver(A);347  cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false);348  EXPECT_TRUE(Actual.empty());349}350 351TEST(CommandLineTest, TokenizeConfigFile9) {352  SmallVector<const char *, 0> Actual;353  BumpPtrAllocator A;354  StringSaver Saver(A);355  cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false);356  EXPECT_TRUE(Actual.empty());357}358 359TEST(CommandLineTest, TokenizeConfigFile10) {360  const char *Input = "\\\nabc";361  const char *const Output[] = { "abc" };362  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);363}364 365TEST(CommandLineTest, TokenizeConfigFile11) {366  const char *Input = "\\\r\nabc";367  const char *const Output[] = { "abc" };368  testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output);369}370 371TEST(CommandLineTest, AliasesWithArguments) {372  static const size_t ARGC = 3;373  const char *const Inputs[][ARGC] = {374    { "-tool", "-actual=x", "-extra" },375    { "-tool", "-actual", "x" },376    { "-tool", "-alias=x", "-extra" },377    { "-tool", "-alias", "x" }378  };379 380  for (size_t i = 0, e = std::size(Inputs); i < e; ++i) {381    StackOption<std::string> Actual("actual");382    StackOption<bool> Extra("extra");383    StackOption<std::string> Input(cl::Positional);384 385    cl::alias Alias("alias", llvm::cl::aliasopt(Actual));386 387    cl::ParseCommandLineOptions(ARGC, Inputs[i]);388    EXPECT_EQ("x", Actual);389    EXPECT_EQ(0, Input.getNumOccurrences());390 391    Alias.removeArgument();392  }393}394 395void testAliasRequired(int argc, const char *const *argv) {396  StackOption<std::string> Option("option", cl::Required);397  cl::alias Alias("o", llvm::cl::aliasopt(Option));398 399  cl::ParseCommandLineOptions(argc, argv);400  EXPECT_EQ("x", Option);401  EXPECT_EQ(1, Option.getNumOccurrences());402 403  Alias.removeArgument();404}405 406TEST(CommandLineTest, AliasRequired) {407  const char *opts1[] = { "-tool", "-option=x" };408  const char *opts2[] = { "-tool", "-o", "x" };409  testAliasRequired(std::size(opts1), opts1);410  testAliasRequired(std::size(opts2), opts2);411}412 413TEST(CommandLineTest, HideUnrelatedOptions) {414  StackOption<int> TestOption1("hide-option-1");415  StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));416 417  cl::HideUnrelatedOptions(TestCategory);418 419  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())420      << "Failed to hide extra option.";421  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())422      << "Hid extra option that should be visable.";423 424  StringMap<cl::Option *> &Map =425      cl::getRegisteredOptions(cl::SubCommand::getTopLevel());426  ASSERT_TRUE(Map.count("help") == (size_t)0 ||427              cl::NotHidden == Map["help"]->getOptionHiddenFlag())428      << "Hid default option that should be visable.";429}430 431cl::OptionCategory TestCategory2("Test Options set 2", "Description");432 433TEST(CommandLineTest, HideUnrelatedOptionsMulti) {434  StackOption<int> TestOption1("multi-hide-option-1");435  StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));436  StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));437 438  const cl::OptionCategory *VisibleCategories[] = {&TestCategory,439                                                   &TestCategory2};440 441  cl::HideUnrelatedOptions(ArrayRef(VisibleCategories));442 443  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())444      << "Failed to hide extra option.";445  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())446      << "Hid extra option that should be visable.";447  ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())448      << "Hid extra option that should be visable.";449 450  StringMap<cl::Option *> &Map =451      cl::getRegisteredOptions(cl::SubCommand::getTopLevel());452  ASSERT_TRUE(Map.count("help") == (size_t)0 ||453              cl::NotHidden == Map["help"]->getOptionHiddenFlag())454      << "Hid default option that should be visable.";455}456 457TEST(CommandLineTest, SetMultiValues) {458  StackOption<int> Option("option");459  const char *args[] = {"prog", "-option=1", "-option=2"};460  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),461                                          &llvm::nulls()));462  EXPECT_EQ(Option, 2);463}464 465TEST(CommandLineTest, SetValueInSubcategories) {466  cl::ResetCommandLineParser();467 468  StackSubCommand SC1("sc1", "First subcommand");469  StackSubCommand SC2("sc2", "Second subcommand");470 471  StackOption<bool> TopLevelOpt("top-level", cl::init(false));472  StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));473  StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));474 475  EXPECT_FALSE(TopLevelOpt);476  EXPECT_FALSE(SC1Opt);477  EXPECT_FALSE(SC2Opt);478  const char *args[] = {"prog", "-top-level"};479  EXPECT_TRUE(480      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));481  EXPECT_TRUE(TopLevelOpt);482  EXPECT_FALSE(SC1Opt);483  EXPECT_FALSE(SC2Opt);484 485  TopLevelOpt = false;486 487  cl::ResetAllOptionOccurrences();488  EXPECT_FALSE(TopLevelOpt);489  EXPECT_FALSE(SC1Opt);490  EXPECT_FALSE(SC2Opt);491  const char *args2[] = {"prog", "sc1", "-sc1"};492  EXPECT_TRUE(493      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));494  EXPECT_FALSE(TopLevelOpt);495  EXPECT_TRUE(SC1Opt);496  EXPECT_FALSE(SC2Opt);497 498  SC1Opt = false;499 500  cl::ResetAllOptionOccurrences();501  EXPECT_FALSE(TopLevelOpt);502  EXPECT_FALSE(SC1Opt);503  EXPECT_FALSE(SC2Opt);504  const char *args3[] = {"prog", "sc2", "-sc2"};505  EXPECT_TRUE(506      cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls()));507  EXPECT_FALSE(TopLevelOpt);508  EXPECT_FALSE(SC1Opt);509  EXPECT_TRUE(SC2Opt);510}511 512TEST(CommandLineTest, LookupFailsInWrongSubCommand) {513  cl::ResetCommandLineParser();514 515  StackSubCommand SC1("sc1", "First subcommand");516  StackSubCommand SC2("sc2", "Second subcommand");517 518  StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));519  StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));520 521  std::string Errs;522  raw_string_ostream OS(Errs);523 524  const char *args[] = {"prog", "sc1", "-sc2"};525  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));526  EXPECT_FALSE(Errs.empty());527}528 529TEST(CommandLineTest, TopLevelOptInSubcommand) {530  enum LiteralOptionEnum {531    foo,532    bar,533    baz,534  };535 536  cl::ResetCommandLineParser();537 538  // This is a top-level option and not associated with a subcommand.539  // A command line using subcommand should parse both subcommand options and540  // top-level options.  A valid use case is that users of llvm command line541  // tools should be able to specify top-level options defined in any library.542  StackOption<std::string> TopLevelOpt("str", cl::init("txt"),543                                       cl::desc("A top-level option."));544 545  StackSubCommand SC("sc", "Subcommand");546  StackOption<std::string> PositionalOpt(547      cl::Positional, cl::desc("positional argument test coverage"),548      cl::sub(SC));549  StackOption<LiteralOptionEnum> LiteralOpt(550      cl::desc("literal argument test coverage"), cl::sub(SC), cl::init(bar),551      cl::values(clEnumVal(foo, "foo"), clEnumVal(bar, "bar"),552                 clEnumVal(baz, "baz")));553  StackOption<bool> EnableOpt("enable", cl::sub(SC), cl::init(false));554  StackOption<int> ThresholdOpt("threshold", cl::sub(SC), cl::init(1));555 556  const char *PositionalOptVal = "input-file";557  const char *args[] = {"prog",    "sc",        PositionalOptVal,558                        "-enable", "--str=csv", "--threshold=2"};559 560  // cl::ParseCommandLineOptions returns true on success. Otherwise, it will561  // print the error message to stderr and exit in this setting (`Errs` ostream562  // is not set).563  ASSERT_TRUE(cl::ParseCommandLineOptions(sizeof(args) / sizeof(args[0]), args,564                                          StringRef()));565  EXPECT_STREQ(PositionalOpt.getValue().c_str(), PositionalOptVal);566  EXPECT_TRUE(EnableOpt);567  // Tests that the value of `str` option is `csv` as specified.568  EXPECT_STREQ(TopLevelOpt.getValue().c_str(), "csv");569  EXPECT_EQ(ThresholdOpt, 2);570 571  for (auto &[LiteralOptVal, WantLiteralOpt] :572       {std::pair{"--bar", bar}, {"--foo", foo}, {"--baz", baz}}) {573    const char *args[] = {"prog", "sc", LiteralOptVal};574    ASSERT_TRUE(cl::ParseCommandLineOptions(sizeof(args) / sizeof(args[0]),575                                            args, StringRef()));576 577    // Tests that literal options are parsed correctly.578    EXPECT_EQ(LiteralOpt, WantLiteralOpt);579  }580}581 582TEST(CommandLineTest, AddToAllSubCommands) {583  cl::ResetCommandLineParser();584 585  StackSubCommand SC1("sc1", "First subcommand");586  StackOption<bool> AllOpt("everywhere", cl::sub(cl::SubCommand::getAll()),587                           cl::init(false));588  StackSubCommand SC2("sc2", "Second subcommand");589 590  EXPECT_TRUE(cl::SubCommand::getTopLevel().OptionsMap.contains("everywhere"));591  EXPECT_TRUE(cl::SubCommand::getAll().OptionsMap.contains("everywhere"));592  EXPECT_TRUE(SC1.OptionsMap.contains("everywhere"));593  EXPECT_TRUE(SC2.OptionsMap.contains("everywhere"));594 595  const char *args[] = {"prog", "-everywhere"};596  const char *args2[] = {"prog", "sc1", "-everywhere"};597  const char *args3[] = {"prog", "sc2", "-everywhere"};598 599  std::string Errs;600  raw_string_ostream OS(Errs);601 602  EXPECT_FALSE(AllOpt);603  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));604  EXPECT_TRUE(AllOpt);605 606  AllOpt = false;607 608  cl::ResetAllOptionOccurrences();609  EXPECT_FALSE(AllOpt);610  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS));611  EXPECT_TRUE(AllOpt);612 613  AllOpt = false;614 615  cl::ResetAllOptionOccurrences();616  EXPECT_FALSE(AllOpt);617  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS));618  EXPECT_TRUE(AllOpt);619 620  // Since all parsing succeeded, the error message should be empty.621  EXPECT_TRUE(Errs.empty());622}623 624TEST(CommandLineTest, ReparseCommandLineOptions) {625  cl::ResetCommandLineParser();626 627  StackOption<bool> TopLevelOpt(628      "top-level", cl::sub(cl::SubCommand::getTopLevel()), cl::init(false));629 630  const char *args[] = {"prog", "-top-level"};631 632  EXPECT_FALSE(TopLevelOpt);633  EXPECT_TRUE(634      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));635  EXPECT_TRUE(TopLevelOpt);636 637  TopLevelOpt = false;638 639  cl::ResetAllOptionOccurrences();640  EXPECT_FALSE(TopLevelOpt);641  EXPECT_TRUE(642      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));643  EXPECT_TRUE(TopLevelOpt);644}645 646TEST(CommandLineTest, RemoveFromRegularSubCommand) {647  cl::ResetCommandLineParser();648 649  StackSubCommand SC("sc", "Subcommand");650  StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));651  StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));652 653  const char *args[] = {"prog", "sc", "-remove-option"};654 655  std::string Errs;656  raw_string_ostream OS(Errs);657 658  EXPECT_FALSE(RemoveOption);659  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));660  EXPECT_TRUE(RemoveOption);661  EXPECT_TRUE(Errs.empty());662 663  RemoveOption.removeArgument();664 665  cl::ResetAllOptionOccurrences();666  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));667  EXPECT_FALSE(Errs.empty());668}669 670TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {671  cl::ResetCommandLineParser();672 673  StackOption<bool> TopLevelRemove("top-level-remove",674                                   cl::sub(cl::SubCommand::getTopLevel()),675                                   cl::init(false));676  StackOption<bool> TopLevelKeep("top-level-keep",677                                 cl::sub(cl::SubCommand::getTopLevel()),678                                 cl::init(false));679 680  const char *args[] = {"prog", "-top-level-remove"};681 682  EXPECT_FALSE(TopLevelRemove);683  EXPECT_TRUE(684      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));685  EXPECT_TRUE(TopLevelRemove);686 687  TopLevelRemove.removeArgument();688 689  cl::ResetAllOptionOccurrences();690  EXPECT_FALSE(691      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));692}693 694TEST(CommandLineTest, RemoveFromAllSubCommands) {695  cl::ResetCommandLineParser();696 697  StackSubCommand SC1("sc1", "First Subcommand");698  StackSubCommand SC2("sc2", "Second Subcommand");699  StackOption<bool> RemoveOption(700      "remove-option", cl::sub(cl::SubCommand::getAll()), cl::init(false));701  StackOption<bool> KeepOption("keep-option", cl::sub(cl::SubCommand::getAll()),702                               cl::init(false));703 704  const char *args0[] = {"prog", "-remove-option"};705  const char *args1[] = {"prog", "sc1", "-remove-option"};706  const char *args2[] = {"prog", "sc2", "-remove-option"};707 708  // It should work for all subcommands including the top-level.709  EXPECT_FALSE(RemoveOption);710  EXPECT_TRUE(711      cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));712  EXPECT_TRUE(RemoveOption);713 714  RemoveOption = false;715 716  cl::ResetAllOptionOccurrences();717  EXPECT_FALSE(RemoveOption);718  EXPECT_TRUE(719      cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));720  EXPECT_TRUE(RemoveOption);721 722  RemoveOption = false;723 724  cl::ResetAllOptionOccurrences();725  EXPECT_FALSE(RemoveOption);726  EXPECT_TRUE(727      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));728  EXPECT_TRUE(RemoveOption);729 730  RemoveOption.removeArgument();731 732  // It should not work for any subcommands including the top-level.733  cl::ResetAllOptionOccurrences();734  EXPECT_FALSE(735      cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));736  cl::ResetAllOptionOccurrences();737  EXPECT_FALSE(738      cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));739  cl::ResetAllOptionOccurrences();740  EXPECT_FALSE(741      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));742}743 744TEST(CommandLineTest, GetRegisteredSubcommands) {745  cl::ResetCommandLineParser();746 747  StackSubCommand SC1("sc1", "First Subcommand");748  StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false));749  StackSubCommand SC2("sc2", "Second subcommand");750  StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false));751 752  const char *args0[] = {"prog", "sc1"};753  const char *args1[] = {"prog", "sc2"};754 755  EXPECT_TRUE(756      cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));757  EXPECT_FALSE(Opt1);758  EXPECT_FALSE(Opt2);759  for (auto *S : cl::getRegisteredSubcommands()) {760    if (*S) {761      EXPECT_EQ("sc1", S->getName());762    }763  }764 765  cl::ResetAllOptionOccurrences();766  EXPECT_TRUE(767      cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls()));768  EXPECT_FALSE(Opt1);769  EXPECT_FALSE(Opt2);770  for (auto *S : cl::getRegisteredSubcommands()) {771    if (*S) {772      EXPECT_EQ("sc2", S->getName());773    }774  }775}776 777TEST(CommandLineTest, DefaultOptions) {778  cl::ResetCommandLineParser();779 780  StackOption<std::string> Bar("bar", cl::sub(cl::SubCommand::getAll()),781                               cl::DefaultOption);782  StackOption<std::string, cl::alias> Bar_Alias(783      "b", cl::desc("Alias for -bar"), cl::aliasopt(Bar), cl::DefaultOption);784 785  StackOption<bool> Foo("foo", cl::init(false),786                        cl::sub(cl::SubCommand::getAll()), cl::DefaultOption);787  StackOption<bool, cl::alias> Foo_Alias("f", cl::desc("Alias for -foo"),788                                         cl::aliasopt(Foo), cl::DefaultOption);789 790  StackSubCommand SC1("sc1", "First Subcommand");791  // Override "-b" and change type in sc1 SubCommand.792  StackOption<bool> SC1_B("b", cl::sub(SC1), cl::init(false));793  StackSubCommand SC2("sc2", "Second subcommand");794  // Override "-foo" and change type in sc2 SubCommand.  Note that this does not795  // affect "-f" alias, which continues to work correctly.796  StackOption<std::string> SC2_Foo("foo", cl::sub(SC2));797 798  const char *args0[] = {"prog", "-b", "args0 bar string", "-f"};799  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args0), args0,800                                          StringRef(), &llvm::nulls()));801  EXPECT_EQ(Bar, "args0 bar string");802  EXPECT_TRUE(Foo);803  EXPECT_FALSE(SC1_B);804  EXPECT_TRUE(SC2_Foo.empty());805 806  cl::ResetAllOptionOccurrences();807 808  const char *args1[] = {"prog", "sc1", "-b", "-bar", "args1 bar string", "-f"};809  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args1), args1,810                                          StringRef(), &llvm::nulls()));811  EXPECT_EQ(Bar, "args1 bar string");812  EXPECT_TRUE(Foo);813  EXPECT_TRUE(SC1_B);814  EXPECT_TRUE(SC2_Foo.empty());815  for (auto *S : cl::getRegisteredSubcommands()) {816    if (*S) {817      EXPECT_EQ("sc1", S->getName());818    }819  }820 821  cl::ResetAllOptionOccurrences();822 823  const char *args2[] = {"prog", "sc2", "-b", "args2 bar string",824                         "-f", "-foo", "foo string"};825  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args2), args2,826                                          StringRef(), &llvm::nulls()));827  EXPECT_EQ(Bar, "args2 bar string");828  EXPECT_TRUE(Foo);829  EXPECT_FALSE(SC1_B);830  EXPECT_EQ(SC2_Foo, "foo string");831  for (auto *S : cl::getRegisteredSubcommands()) {832    if (*S) {833      EXPECT_EQ("sc2", S->getName());834    }835  }836  cl::ResetCommandLineParser();837}838 839TEST(CommandLineTest, ArgumentLimit) {840#if HAVE_UNISTD_H && defined(_SC_ARG_MAX)841  if (sysconf(_SC_ARG_MAX) != -1) {842#endif843    std::string args(32 * 4096, 'a');844    EXPECT_FALSE(845        llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data()));846#if HAVE_UNISTD_H && defined(_SC_ARG_MAX)847  }848#endif849  std::string args2(256, 'a');850  EXPECT_TRUE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args2.data()));851}852 853TEST(CommandLineTest, ArgumentLimitWindows) {854  Triple processTriple(sys::getProcessTriple());855  if (!processTriple.isOSWindows() ||856      processTriple.isWindowsCygwinEnvironment())857    GTEST_SKIP();858  // We use 32000 as a limit for command line length. Program name ('cl'),859  // separating spaces and termination null character occupy 5 symbols.860  std::string long_arg(32000 - 5, 'b');861  EXPECT_TRUE(862      llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data()));863  long_arg += 'b';864  EXPECT_FALSE(865      llvm::sys::commandLineFitsWithinSystemLimits("cl", long_arg.data()));866}867 868TEST(CommandLineTest, ResponseFileWindows) {869  Triple processTriple(sys::getProcessTriple());870  if (!processTriple.isOSWindows() ||871      processTriple.isWindowsCygwinEnvironment())872    GTEST_SKIP();873 874  StackOption<std::string, cl::list<std::string>> InputFilenames(875      cl::Positional, cl::desc("<input files>"));876  StackOption<bool> TopLevelOpt("top-level", cl::init(false));877 878  // Create response file.879  TempFile ResponseFile("resp-", ".txt",880                        "-top-level\npath\\dir\\file1\npath/dir/file2",881                        /*Unique*/ true);882 883  llvm::SmallString<128> RspOpt;884  RspOpt.append(1, '@');885  RspOpt.append(ResponseFile.path());886  const char *args[] = {"prog", RspOpt.c_str()};887  EXPECT_FALSE(TopLevelOpt);888  EXPECT_TRUE(889      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));890  EXPECT_TRUE(TopLevelOpt);891  EXPECT_EQ(InputFilenames[0], "path\\dir\\file1");892  EXPECT_EQ(InputFilenames[1], "path/dir/file2");893}894 895TEST(CommandLineTest, ResponseFiles) {896  vfs::InMemoryFileSystem FS;897#ifdef _WIN32898  const char *TestRoot = "C:\\";899#else900  const char *TestRoot = "/";901#endif902  FS.setCurrentWorkingDirectory(TestRoot);903 904  // Create included response file of first level.905  llvm::StringRef IncludedFileName = "resp1";906  FS.addFile(IncludedFileName, 0,907             llvm::MemoryBuffer::getMemBuffer("-option_1 -option_2\n"908                                              "@incdir/resp2\n"909                                              "-option_3=abcd\n"910                                              "@incdir/resp3\n"911                                              "-option_4=efjk\n"));912 913  // Directory for included file.914  llvm::StringRef IncDir = "incdir";915 916  // Create included response file of second level.917  llvm::SmallString<128> IncludedFileName2;918  llvm::sys::path::append(IncludedFileName2, IncDir, "resp2");919  FS.addFile(IncludedFileName2, 0,920             MemoryBuffer::getMemBuffer("-option_21 -option_22\n"921                                        "-option_23=abcd\n"));922 923  // Create second included response file of second level.924  llvm::SmallString<128> IncludedFileName3;925  llvm::sys::path::append(IncludedFileName3, IncDir, "resp3");926  FS.addFile(IncludedFileName3, 0,927             MemoryBuffer::getMemBuffer("-option_31 -option_32\n"928                                        "-option_33=abcd\n"));929 930  // Prepare 'file' with reference to response file.931  SmallString<128> IncRef;932  IncRef.append(1, '@');933  IncRef.append(IncludedFileName);934  llvm::SmallVector<const char *, 4> Argv = {"test/test", "-flag_1",935                                             IncRef.c_str(), "-flag_2"};936 937  // Expand response files.938  llvm::BumpPtrAllocator A;939  llvm::cl::ExpansionContext ECtx(A, llvm::cl::TokenizeGNUCommandLine);940  ECtx.setVFS(&FS).setCurrentDir(TestRoot).setRelativeNames(true);941  ASSERT_FALSE((bool)ECtx.expandResponseFiles(Argv));942  EXPECT_THAT(Argv, testing::Pointwise(943                        StringEquality(),944                        {"test/test", "-flag_1", "-option_1", "-option_2",945                         "-option_21", "-option_22", "-option_23=abcd",946                         "-option_3=abcd", "-option_31", "-option_32",947                         "-option_33=abcd", "-option_4=efjk", "-flag_2"}));948}949 950TEST(CommandLineTest, RecursiveResponseFiles) {951  vfs::InMemoryFileSystem FS;952#ifdef _WIN32953  const char *TestRoot = "C:\\";954#else955  const char *TestRoot = "/";956#endif957  FS.setCurrentWorkingDirectory(TestRoot);958 959  StringRef SelfFilePath = "self.rsp";960  std::string SelfFileRef = ("@" + SelfFilePath).str();961 962  StringRef NestedFilePath = "nested.rsp";963  std::string NestedFileRef = ("@" + NestedFilePath).str();964 965  StringRef FlagFilePath = "flag.rsp";966  std::string FlagFileRef = ("@" + FlagFilePath).str();967 968  std::string SelfFileContents;969  raw_string_ostream SelfFile(SelfFileContents);970  SelfFile << "-option_1\n";971  SelfFile << FlagFileRef << "\n";972  SelfFile << NestedFileRef << "\n";973  SelfFile << SelfFileRef << "\n";974  FS.addFile(SelfFilePath, 0, MemoryBuffer::getMemBuffer(SelfFile.str()));975 976  std::string NestedFileContents;977  raw_string_ostream NestedFile(NestedFileContents);978  NestedFile << "-option_2\n";979  NestedFile << FlagFileRef << "\n";980  NestedFile << SelfFileRef << "\n";981  NestedFile << NestedFileRef << "\n";982  FS.addFile(NestedFilePath, 0, MemoryBuffer::getMemBuffer(NestedFile.str()));983 984  std::string FlagFileContents;985  raw_string_ostream FlagFile(FlagFileContents);986  FlagFile << "-option_x\n";987  FS.addFile(FlagFilePath, 0, MemoryBuffer::getMemBuffer(FlagFile.str()));988 989  // Ensure:990  // Recursive expansion terminates991  // Recursive files never expand992  // Non-recursive repeats are allowed993  SmallVector<const char *, 4> Argv = {"test/test", SelfFileRef.c_str(),994                                       "-option_3"};995  BumpPtrAllocator A;996#ifdef _WIN32997  cl::TokenizerCallback Tokenizer = cl::TokenizeWindowsCommandLine;998#else999  cl::TokenizerCallback Tokenizer = cl::TokenizeGNUCommandLine;1000#endif1001  llvm::cl::ExpansionContext ECtx(A, Tokenizer);1002  ECtx.setVFS(&FS).setCurrentDir(TestRoot);1003  llvm::Error Err = ECtx.expandResponseFiles(Argv);1004  ASSERT_TRUE((bool)Err);1005  SmallString<128> FilePath = SelfFilePath;1006  std::error_code EC = FS.makeAbsolute(FilePath);1007  ASSERT_FALSE((bool)EC);1008  std::string ExpectedMessage =1009      std::string("recursive expansion of: '") + std::string(FilePath) + "'";1010  ASSERT_TRUE(toString(std::move(Err)) == ExpectedMessage);1011 1012  EXPECT_THAT(Argv,1013              testing::Pointwise(StringEquality(),1014                                 {"test/test", "-option_1", "-option_x",1015                                  "-option_2", "-option_x", SelfFileRef.c_str(),1016                                  NestedFileRef.c_str(), SelfFileRef.c_str(),1017                                  "-option_3"}));1018}1019 1020TEST(CommandLineTest, ResponseFilesAtArguments) {1021  vfs::InMemoryFileSystem FS;1022#ifdef _WIN321023  const char *TestRoot = "C:\\";1024#else1025  const char *TestRoot = "/";1026#endif1027  FS.setCurrentWorkingDirectory(TestRoot);1028 1029  StringRef ResponseFilePath = "test.rsp";1030 1031  std::string ResponseFileContents;1032  raw_string_ostream ResponseFile(ResponseFileContents);1033  ResponseFile << "-foo" << "\n";1034  ResponseFile << "-bar" << "\n";1035  FS.addFile(ResponseFilePath, 0,1036             MemoryBuffer::getMemBuffer(ResponseFile.str()));1037 1038  // Ensure we expand rsp files after lots of non-rsp arguments starting with @.1039  constexpr size_t NON_RSP_AT_ARGS = 64;1040  SmallVector<const char *, 4> Argv = {"test/test"};1041  Argv.append(NON_RSP_AT_ARGS, "@non_rsp_at_arg");1042  std::string ResponseFileRef = ("@" + ResponseFilePath).str();1043  Argv.push_back(ResponseFileRef.c_str());1044 1045  BumpPtrAllocator A;1046  llvm::cl::ExpansionContext ECtx(A, cl::TokenizeGNUCommandLine);1047  ECtx.setVFS(&FS).setCurrentDir(TestRoot);1048  ASSERT_FALSE((bool)ECtx.expandResponseFiles(Argv));1049 1050  // ASSERT instead of EXPECT to prevent potential out-of-bounds access.1051  ASSERT_EQ(Argv.size(), 1 + NON_RSP_AT_ARGS + 2);1052  size_t i = 0;1053  EXPECT_STREQ(Argv[i++], "test/test");1054  for (; i < 1 + NON_RSP_AT_ARGS; ++i)1055    EXPECT_STREQ(Argv[i], "@non_rsp_at_arg");1056  EXPECT_STREQ(Argv[i++], "-foo");1057  EXPECT_STREQ(Argv[i++], "-bar");1058}1059 1060TEST(CommandLineTest, ResponseFileRelativePath) {1061  vfs::InMemoryFileSystem FS;1062#ifdef _WIN321063  const char *TestRoot = "C:\\";1064#else1065  const char *TestRoot = "//net";1066#endif1067  FS.setCurrentWorkingDirectory(TestRoot);1068 1069  StringRef OuterFile = "dir/outer.rsp";1070  StringRef OuterFileContents = "@inner.rsp";1071  FS.addFile(OuterFile, 0, MemoryBuffer::getMemBuffer(OuterFileContents));1072 1073  StringRef InnerFile = "dir/inner.rsp";1074  StringRef InnerFileContents = "-flag";1075  FS.addFile(InnerFile, 0, MemoryBuffer::getMemBuffer(InnerFileContents));1076 1077  SmallVector<const char *, 2> Argv = {"test/test", "@dir/outer.rsp"};1078 1079  BumpPtrAllocator A;1080  llvm::cl::ExpansionContext ECtx(A, cl::TokenizeGNUCommandLine);1081  ECtx.setVFS(&FS).setCurrentDir(TestRoot).setRelativeNames(true);1082  ASSERT_FALSE((bool)ECtx.expandResponseFiles(Argv));1083  EXPECT_THAT(Argv,1084              testing::Pointwise(StringEquality(), {"test/test", "-flag"}));1085}1086 1087TEST(CommandLineTest, ResponseFileEOLs) {1088  vfs::InMemoryFileSystem FS;1089#ifdef _WIN321090  const char *TestRoot = "C:\\";1091#else1092  const char *TestRoot = "//net";1093#endif1094  FS.setCurrentWorkingDirectory(TestRoot);1095  FS.addFile("eols.rsp", 0,1096             MemoryBuffer::getMemBuffer("-Xclang -Wno-whatever\n input.cpp"));1097  SmallVector<const char *, 2> Argv = {"clang", "@eols.rsp"};1098  BumpPtrAllocator A;1099  llvm::cl::ExpansionContext ECtx(A, cl::TokenizeWindowsCommandLine);1100  ECtx.setVFS(&FS).setCurrentDir(TestRoot).setMarkEOLs(true).setRelativeNames(1101      true);1102  ASSERT_FALSE((bool)ECtx.expandResponseFiles(Argv));1103  const char *Expected[] = {"clang", "-Xclang", "-Wno-whatever", nullptr,1104                            "input.cpp"};1105  ASSERT_EQ(std::size(Expected), Argv.size());1106  for (size_t I = 0, E = std::size(Expected); I < E; ++I) {1107    if (Expected[I] == nullptr) {1108      ASSERT_EQ(Argv[I], nullptr);1109    } else {1110      ASSERT_STREQ(Expected[I], Argv[I]);1111    }1112  }1113}1114 1115TEST(CommandLineTest, BadResponseFile) {1116  BumpPtrAllocator A;1117  StringSaver Saver(A);1118  TempDir ADir("dir", /*Unique*/ true);1119  SmallString<128> AFilePath = ADir.path();1120  llvm::sys::path::append(AFilePath, "file.rsp");1121  std::string AFileExp = std::string("@") + std::string(AFilePath.str());1122  SmallVector<const char *, 2> Argv = {"clang", AFileExp.c_str()};1123 1124  bool Res = cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);1125  ASSERT_TRUE(Res);1126  ASSERT_EQ(2U, Argv.size());1127  ASSERT_STREQ(Argv[0], "clang");1128  ASSERT_STREQ(Argv[1], AFileExp.c_str());1129 1130  std::string ADirExp = std::string("@") + std::string(ADir.path());1131  Argv = {"clang", ADirExp.c_str()};1132  Res = cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);1133  ASSERT_FALSE(Res);1134  ASSERT_EQ(2U, Argv.size());1135  ASSERT_STREQ(Argv[0], "clang");1136  ASSERT_STREQ(Argv[1], ADirExp.c_str());1137}1138 1139TEST(CommandLineTest, SetDefaultValue) {1140  cl::ResetCommandLineParser();1141 1142  StackOption<std::string> Opt1("opt1", cl::init("true"));1143  StackOption<bool> Opt2("opt2", cl::init(true));1144  cl::alias Alias("alias", llvm::cl::aliasopt(Opt2));1145  StackOption<int> Opt3("opt3", cl::init(3));1146 1147  llvm::SmallVector<int, 3> IntVals = {1, 2, 3};1148  llvm::SmallVector<std::string, 3> StrVals = {"foo", "bar", "baz"};1149 1150  StackOption<int, cl::list<int>> List1(1151      "list1", cl::list_init<int>(llvm::ArrayRef<int>(IntVals)),1152      cl::CommaSeparated);1153  StackOption<std::string, cl::list<std::string>> List2(1154      "list2", cl::list_init<std::string>(llvm::ArrayRef<std::string>(StrVals)),1155      cl::CommaSeparated);1156  cl::alias ListAlias("list-alias", llvm::cl::aliasopt(List2));1157 1158  const char *args[] = {"prog",   "-opt1=false", "-list1", "4",1159                        "-list1", "5,6",         "-opt2",  "-opt3"};1160 1161  EXPECT_TRUE(1162      cl::ParseCommandLineOptions(7, args, StringRef(), &llvm::nulls()));1163 1164  EXPECT_EQ(Opt1, "false");1165  EXPECT_TRUE(Opt2);1166  EXPECT_EQ(Opt3, 3);1167 1168  for (size_t I = 0, E = IntVals.size(); I < E; ++I) {1169    EXPECT_EQ(IntVals[I] + 3, List1[I]);1170    EXPECT_EQ(StrVals[I], List2[I]);1171  }1172 1173  Opt2 = false;1174  Opt3 = 1;1175 1176  cl::ResetAllOptionOccurrences();1177 1178  for (auto &OM : cl::getRegisteredOptions(cl::SubCommand::getTopLevel())) {1179    cl::Option *O = OM.second;1180    if (O->ArgStr == "opt2") {1181      continue;1182    }1183    O->setDefault();1184  }1185 1186  EXPECT_EQ(Opt1, "true");1187  EXPECT_TRUE(Opt2);1188  EXPECT_EQ(Opt3, 3);1189  for (size_t I = 0, E = IntVals.size(); I < E; ++I) {1190    EXPECT_EQ(IntVals[I], List1[I]);1191    EXPECT_EQ(StrVals[I], List2[I]);1192  }1193 1194  Alias.removeArgument();1195  ListAlias.removeArgument();1196}1197 1198TEST(CommandLineTest, ReadConfigFile) {1199  llvm::SmallVector<const char *, 1> Argv;1200 1201  TempDir TestDir("unittest", /*Unique*/ true);1202  TempDir TestSubDir(TestDir.path("subdir"), /*Unique*/ false);1203 1204  llvm::SmallString<128> TestCfg = TestDir.path("foo");1205  TempFile ConfigFile(TestCfg, "",1206                      "# Comment\n"1207                      "-option_1\n"1208                      "-option_2=<CFGDIR>/dir1\n"1209                      "-option_3=<CFGDIR>\n"1210                      "-option_4 <CFGDIR>\n"1211                      "-option_5=<CFG\\\n"1212                      "DIR>\n"1213                      "-option_6=<CFGDIR>/dir1,<CFGDIR>/dir2\n"1214                      "@subconfig\n"1215                      "-option_11=abcd\n"1216                      "-option_12=\\\n"1217                      "cdef\n");1218 1219  llvm::SmallString<128> TestCfg2 = TestDir.path("subconfig");1220  TempFile ConfigFile2(TestCfg2, "",1221                       "-option_7\n"1222                       "-option_8=<CFGDIR>/dir2\n"1223                       "@subdir/subfoo\n"1224                       "\n"1225                       "   # comment\n");1226 1227  llvm::SmallString<128> TestCfg3 = TestSubDir.path("subfoo");1228  TempFile ConfigFile3(TestCfg3, "",1229                       "-option_9=<CFGDIR>/dir3\n"1230                       "@<CFGDIR>/subfoo2\n");1231 1232  llvm::SmallString<128> TestCfg4 = TestSubDir.path("subfoo2");1233  TempFile ConfigFile4(TestCfg4, "", "-option_10\n");1234 1235  // Make sure the current directory is not the directory where config files1236  // resides. In this case the code that expands response files will not find1237  // 'subconfig' unless it resolves nested inclusions relative to the including1238  // file.1239  llvm::SmallString<128> CurrDir;1240  std::error_code EC = llvm::sys::fs::current_path(CurrDir);1241  EXPECT_TRUE(!EC);1242  EXPECT_NE(CurrDir.str(), TestDir.path());1243 1244  llvm::BumpPtrAllocator A;1245  llvm::cl::ExpansionContext ECtx(A, cl::tokenizeConfigFile);1246  llvm::Error Result = ECtx.readConfigFile(ConfigFile.path(), Argv);1247 1248  EXPECT_FALSE((bool)Result);1249  EXPECT_EQ(Argv.size(), 13U);1250  EXPECT_STREQ(Argv[0], "-option_1");1251  EXPECT_STREQ(Argv[1],1252               ("-option_2=" + TestDir.path() + "/dir1").str().c_str());1253  EXPECT_STREQ(Argv[2], ("-option_3=" + TestDir.path()).str().c_str());1254  EXPECT_STREQ(Argv[3], "-option_4");1255  EXPECT_STREQ(Argv[4], TestDir.path().str().c_str());1256  EXPECT_STREQ(Argv[5], ("-option_5=" + TestDir.path()).str().c_str());1257  EXPECT_STREQ(Argv[6], ("-option_6=" + TestDir.path() + "/dir1," +1258                         TestDir.path() + "/dir2")1259                            .str()1260                            .c_str());1261  EXPECT_STREQ(Argv[7], "-option_7");1262  EXPECT_STREQ(Argv[8],1263               ("-option_8=" + TestDir.path() + "/dir2").str().c_str());1264  EXPECT_STREQ(Argv[9],1265               ("-option_9=" + TestSubDir.path() + "/dir3").str().c_str());1266  EXPECT_STREQ(Argv[10], "-option_10");1267  EXPECT_STREQ(Argv[11], "-option_11=abcd");1268  EXPECT_STREQ(Argv[12], "-option_12=cdef");1269}1270 1271TEST(CommandLineTest, PositionalEatArgsError) {1272  cl::ResetCommandLineParser();1273 1274  StackOption<std::string, cl::list<std::string>> PosEatArgs(1275      "positional-eat-args", cl::Positional, cl::desc("<arguments>..."),1276      cl::PositionalEatsArgs);1277  StackOption<std::string, cl::list<std::string>> PosEatArgs2(1278      "positional-eat-args2", cl::Positional, cl::desc("Some strings"),1279      cl::PositionalEatsArgs);1280 1281  const char *args[] = {"prog", "-positional-eat-args=XXXX"};1282  const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"};1283  const char *args3[] = {"prog", "-positional-eat-args", "-foo"};1284  const char *args4[] = {"prog", "-positional-eat-args",1285                         "-foo", "-positional-eat-args2",1286                         "-bar", "foo"};1287 1288  std::string Errs;1289  raw_string_ostream OS(Errs);1290  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));1291  EXPECT_FALSE(Errs.empty()); Errs.clear();1292  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS));1293  EXPECT_FALSE(Errs.empty()); Errs.clear();1294  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS));1295  EXPECT_TRUE(Errs.empty()); Errs.clear();1296 1297  cl::ResetAllOptionOccurrences();1298  EXPECT_TRUE(cl::ParseCommandLineOptions(6, args4, StringRef(), &OS));1299  EXPECT_EQ(PosEatArgs.size(), 1u);1300  EXPECT_EQ(PosEatArgs2.size(), 2u);1301  EXPECT_TRUE(Errs.empty());1302}1303 1304#ifdef _WIN321305void checkSeparators(StringRef Path) {1306  char UndesiredSeparator = sys::path::get_separator()[0] == '/' ? '\\' : '/';1307  ASSERT_EQ(Path.find(UndesiredSeparator), StringRef::npos);1308}1309 1310TEST(CommandLineTest, GetCommandLineArguments) {1311  int argc = __argc;1312  char **argv = __argv;1313 1314  // GetCommandLineArguments is called in InitLLVM.1315  llvm::InitLLVM X(argc, argv);1316 1317  EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]),1318            llvm::sys::path::is_absolute(__argv[0]));1319  checkSeparators(argv[0]);1320 1321  EXPECT_TRUE(1322      llvm::sys::path::filename(argv[0]).equals_insensitive("supporttests.exe"))1323      << "Filename of test executable is "1324      << llvm::sys::path::filename(argv[0]);1325}1326#endif1327 1328class OutputRedirector {1329public:1330  OutputRedirector(int RedirectFD)1331      : RedirectFD(RedirectFD), OldFD(dup(RedirectFD)) {1332    if (OldFD == -1 ||1333        sys::fs::createTemporaryFile("unittest-redirect", "", NewFD,1334                                     FilePath) ||1335        dup2(NewFD, RedirectFD) == -1)1336      Valid = false;1337  }1338 1339  ~OutputRedirector() {1340    dup2(OldFD, RedirectFD);1341    close(OldFD);1342    close(NewFD);1343  }1344 1345  SmallVector<char, 128> FilePath;1346  bool Valid = true;1347 1348private:1349  int RedirectFD;1350  int OldFD;1351  int NewFD;1352};1353 1354struct AutoDeleteFile {1355  SmallVector<char, 128> FilePath;1356  ~AutoDeleteFile() {1357    if (!FilePath.empty())1358      sys::fs::remove(std::string(FilePath.data(), FilePath.size()));1359  }1360};1361 1362static std::string interceptStdout(std::function<void()> F) {1363  outs().flush(); // flush any output from previous tests1364  AutoDeleteFile File;1365  {1366    OutputRedirector Stdout(fileno(stdout));1367    if (!Stdout.Valid)1368      return "";1369    File.FilePath = Stdout.FilePath;1370    F();1371    outs().flush();1372  }1373  auto Buffer = MemoryBuffer::getFile(File.FilePath);1374  if (!Buffer)1375    return "";1376  return Buffer->get()->getBuffer().str();1377}1378 1379template <void (*Func)(const cl::Option &)>1380class PrintOptionTestBase : public ::testing::Test {1381public:1382  // Return std::string because the output of a failing EXPECT check is1383  // unreadable for StringRef. It also avoids any lifetime issues.1384  template <typename... Ts> std::string runTest(Ts... OptionAttributes) {1385    StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),1386                                        OptionAttributes...);1387    return interceptStdout([&]() { Func(TestOption); });1388  }1389 1390  enum class OptionValue { Val };1391  const StringRef Opt = "some-option";1392  const StringRef HelpText = "some help";1393};1394 1395  // This is a workaround for cl::Option sub-classes having their1396  // printOptionInfo functions private.1397void printOptionInfo(const cl::Option &O) {1398  O.printOptionInfo(/*GlobalWidth=*/26);1399}1400 1401using PrintOptionInfoTest = PrintOptionTestBase<printOptionInfo>;1402 1403TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithoutSentinel) {1404  std::string Output =1405      runTest(cl::ValueOptional,1406              cl::values(clEnumValN(OptionValue::Val, "v1", "desc1")));1407 1408  // clang-format off1409  EXPECT_EQ(Output, ("  --" + Opt + "=<value> - " + HelpText + "\n"1410                     "    =v1                 -   desc1\n")1411                        .str());1412  // clang-format on1413}1414 1415TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinel) {1416  std::string Output = runTest(1417      cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),1418                                    clEnumValN(OptionValue::Val, "", "")));1419 1420  // clang-format off1421  EXPECT_EQ(Output,1422            ("  --" + Opt + "         - " + HelpText + "\n"1423             "  --" + Opt + "=<value> - " + HelpText + "\n"1424             "    =v1                 -   desc1\n")1425                .str());1426  // clang-format on1427}1428 1429TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinelWithHelp) {1430  std::string Output = runTest(1431      cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),1432                                    clEnumValN(OptionValue::Val, "", "desc2")));1433 1434  // clang-format off1435  EXPECT_EQ(Output, ("  --" + Opt + "         - " + HelpText + "\n"1436                     "  --" + Opt + "=<value> - " + HelpText + "\n"1437                     "    =v1                 -   desc1\n"1438                     "    =<empty>            -   desc2\n")1439                        .str());1440  // clang-format on1441}1442 1443TEST_F(PrintOptionInfoTest, PrintOptionInfoValueRequiredWithEmptyValueName) {1444  std::string Output = runTest(1445      cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"),1446                                    clEnumValN(OptionValue::Val, "", "")));1447 1448  // clang-format off1449  EXPECT_EQ(Output, ("  --" + Opt + "=<value> - " + HelpText + "\n"1450                     "    =v1                 -   desc1\n"1451                     "    =<empty>\n")1452                        .str());1453  // clang-format on1454}1455 1456TEST_F(PrintOptionInfoTest, PrintOptionInfoEmptyValueDescription) {1457  std::string Output = runTest(1458      cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "")));1459 1460  // clang-format off1461  EXPECT_EQ(Output,1462            ("  --" + Opt + "=<value> - " + HelpText + "\n"1463             "    =v1\n").str());1464  // clang-format on1465}1466 1467TEST_F(PrintOptionInfoTest, PrintOptionInfoMultilineValueDescription) {1468  std::string Output =1469      runTest(cl::ValueRequired,1470              cl::values(clEnumValN(OptionValue::Val, "v1",1471                                    "This is the first enum value\n"1472                                    "which has a really long description\n"1473                                    "thus it is multi-line."),1474                         clEnumValN(OptionValue::Val, "",1475                                    "This is an unnamed enum value\n"1476                                    "Should be indented as well")));1477 1478  // clang-format off1479  EXPECT_EQ(Output,1480            ("  --" + Opt + "=<value> - " + HelpText + "\n"1481             "    =v1                 -   This is the first enum value\n"1482             "                            which has a really long description\n"1483             "                            thus it is multi-line.\n"1484             "    =<empty>            -   This is an unnamed enum value\n"1485             "                            Should be indented as well\n").str());1486  // clang-format on1487}1488 1489void printOptionValue(const cl::Option &O) {1490  O.printOptionValue(/*GlobalWidth=*/12, /*Force=*/true);1491}1492 1493using PrintOptionValueTest = PrintOptionTestBase<printOptionValue>;1494 1495TEST_F(PrintOptionValueTest, PrintOptionDefaultValue) {1496  std::string Output =1497      runTest(cl::init(OptionValue::Val),1498              cl::values(clEnumValN(OptionValue::Val, "v1", "desc1")));1499 1500  EXPECT_EQ(Output, ("    --" + Opt + " = v1       (default: v1)\n").str());1501}1502 1503TEST_F(PrintOptionValueTest, PrintOptionNoDefaultValue) {1504  std::string Output =1505      runTest(cl::values(clEnumValN(OptionValue::Val, "v1", "desc1")));1506 1507  // Note: the option still has a (zero-initialized) value, but the default1508  // is invalid and doesn't match any value.1509  EXPECT_EQ(Output, ("    --" + Opt + " = v1       (default: )\n").str());1510}1511 1512TEST_F(PrintOptionValueTest, PrintOptionUnknownValue) {1513  std::string Output = runTest(cl::init(OptionValue::Val));1514 1515  EXPECT_EQ(Output, ("    --" + Opt + " = *unknown option value*\n").str());1516}1517 1518class GetOptionWidthTest : public ::testing::Test {1519public:1520  enum class OptionValue { Val };1521 1522  template <typename... Ts>1523  size_t runTest(StringRef ArgName, Ts... OptionAttributes) {1524    StackOption<OptionValue> TestOption(ArgName, cl::desc("some help"),1525                                        OptionAttributes...);1526    return getOptionWidth(TestOption);1527  }1528 1529private:1530  // This is a workaround for cl::Option sub-classes having their1531  // printOptionInfo1532  // functions private.1533  size_t getOptionWidth(const cl::Option &O) { return O.getOptionWidth(); }1534};1535 1536TEST_F(GetOptionWidthTest, GetOptionWidthArgNameLonger) {1537  StringRef ArgName("a-long-argument-name");1538  size_t ExpectedStrSize = ("  --" + ArgName + "=<value> - ").str().size();1539  EXPECT_EQ(1540      runTest(ArgName, cl::values(clEnumValN(OptionValue::Val, "v", "help"))),1541      ExpectedStrSize);1542}1543 1544TEST_F(GetOptionWidthTest, GetOptionWidthFirstOptionNameLonger) {1545  StringRef OptName("a-long-option-name");1546  size_t ExpectedStrSize = ("    =" + OptName + " - ").str().size();1547  EXPECT_EQ(1548      runTest("a", cl::values(clEnumValN(OptionValue::Val, OptName, "help"),1549                              clEnumValN(OptionValue::Val, "b", "help"))),1550      ExpectedStrSize);1551}1552 1553TEST_F(GetOptionWidthTest, GetOptionWidthSecondOptionNameLonger) {1554  StringRef OptName("a-long-option-name");1555  size_t ExpectedStrSize = ("    =" + OptName + " - ").str().size();1556  EXPECT_EQ(1557      runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"),1558                              clEnumValN(OptionValue::Val, OptName, "help"))),1559      ExpectedStrSize);1560}1561 1562TEST_F(GetOptionWidthTest, GetOptionWidthEmptyOptionNameLonger) {1563  size_t ExpectedStrSize = StringRef("    =<empty> - ").size();1564  // The length of a=<value> (including indentation) is actually the same as the1565  // =<empty> string, so it is impossible to distinguish via testing the case1566  // where the empty string is picked from where the option name is picked.1567  EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"),1568                                    clEnumValN(OptionValue::Val, "", "help"))),1569            ExpectedStrSize);1570}1571 1572TEST_F(GetOptionWidthTest,1573       GetOptionWidthValueOptionalEmptyOptionWithNoDescription) {1574  StringRef ArgName("a");1575  // The length of a=<value> (including indentation) is actually the same as the1576  // =<empty> string, so it is impossible to distinguish via testing the case1577  // where the empty string is ignored from where it is not ignored.1578  // The dash will not actually be printed, but the space it would take up is1579  // included to ensure a consistent column width.1580  size_t ExpectedStrSize = ("  -" + ArgName + "=<value> - ").str().size();1581  EXPECT_EQ(runTest(ArgName, cl::ValueOptional,1582                    cl::values(clEnumValN(OptionValue::Val, "value", "help"),1583                               clEnumValN(OptionValue::Val, "", ""))),1584            ExpectedStrSize);1585}1586 1587TEST_F(GetOptionWidthTest,1588       GetOptionWidthValueRequiredEmptyOptionWithNoDescription) {1589  // The length of a=<value> (including indentation) is actually the same as the1590  // =<empty> string, so it is impossible to distinguish via testing the case1591  // where the empty string is picked from where the option name is picked1592  size_t ExpectedStrSize = StringRef("    =<empty> - ").size();1593  EXPECT_EQ(runTest("a", cl::ValueRequired,1594                    cl::values(clEnumValN(OptionValue::Val, "value", "help"),1595                               clEnumValN(OptionValue::Val, "", ""))),1596            ExpectedStrSize);1597}1598 1599TEST(CommandLineTest, PrefixOptions) {1600  cl::ResetCommandLineParser();1601 1602  StackOption<std::string, cl::list<std::string>> IncludeDirs(1603      "I", cl::Prefix, cl::desc("Declare an include directory"));1604 1605  // Test non-prefixed variant works with cl::Prefix options.1606  EXPECT_TRUE(IncludeDirs.empty());1607  const char *args[] = {"prog", "-I=/usr/include"};1608  EXPECT_TRUE(1609      cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));1610  EXPECT_EQ(IncludeDirs.size(), 1u);1611  EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);1612 1613  IncludeDirs.erase(IncludeDirs.begin());1614  cl::ResetAllOptionOccurrences();1615 1616  // Test non-prefixed variant works with cl::Prefix options when value is1617  // passed in following argument.1618  EXPECT_TRUE(IncludeDirs.empty());1619  const char *args2[] = {"prog", "-I", "/usr/include"};1620  EXPECT_TRUE(1621      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));1622  EXPECT_EQ(IncludeDirs.size(), 1u);1623  EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);1624 1625  IncludeDirs.erase(IncludeDirs.begin());1626  cl::ResetAllOptionOccurrences();1627 1628  // Test prefixed variant works with cl::Prefix options.1629  EXPECT_TRUE(IncludeDirs.empty());1630  const char *args3[] = {"prog", "-I/usr/include"};1631  EXPECT_TRUE(1632      cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));1633  EXPECT_EQ(IncludeDirs.size(), 1u);1634  EXPECT_EQ(IncludeDirs.front().compare("/usr/include"), 0);1635 1636  StackOption<std::string, cl::list<std::string>> MacroDefs(1637      "D", cl::AlwaysPrefix, cl::desc("Define a macro"),1638      cl::value_desc("MACRO[=VALUE]"));1639 1640  cl::ResetAllOptionOccurrences();1641 1642  // Test non-prefixed variant does not work with cl::AlwaysPrefix options:1643  // equal sign is part of the value.1644  EXPECT_TRUE(MacroDefs.empty());1645  const char *args4[] = {"prog", "-D=HAVE_FOO"};1646  EXPECT_TRUE(1647      cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));1648  EXPECT_EQ(MacroDefs.size(), 1u);1649  EXPECT_EQ(MacroDefs.front().compare("=HAVE_FOO"), 0);1650 1651  MacroDefs.erase(MacroDefs.begin());1652  cl::ResetAllOptionOccurrences();1653 1654  // Test non-prefixed variant does not allow value to be passed in following1655  // argument with cl::AlwaysPrefix options.1656  EXPECT_TRUE(MacroDefs.empty());1657  const char *args5[] = {"prog", "-D", "HAVE_FOO"};1658  EXPECT_FALSE(1659      cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls()));1660  EXPECT_TRUE(MacroDefs.empty());1661 1662  cl::ResetAllOptionOccurrences();1663 1664  // Test prefixed variant works with cl::AlwaysPrefix options.1665  EXPECT_TRUE(MacroDefs.empty());1666  const char *args6[] = {"prog", "-DHAVE_FOO"};1667  EXPECT_TRUE(1668      cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));1669  EXPECT_EQ(MacroDefs.size(), 1u);1670  EXPECT_EQ(MacroDefs.front().compare("HAVE_FOO"), 0);1671}1672 1673TEST(CommandLineTest, GroupingWithValue) {1674  cl::ResetCommandLineParser();1675 1676  StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag"));1677  StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag"));1678  StackOption<bool> OptD("d", cl::Grouping, cl::ValueDisallowed,1679                         cl::desc("ValueDisallowed option"));1680  StackOption<std::string> OptV("v", cl::Grouping,1681                                cl::desc("ValueRequired option"));1682  StackOption<std::string> OptO("o", cl::Grouping, cl::ValueOptional,1683                                cl::desc("ValueOptional option"));1684 1685  // Should be possible to use an option which requires a value1686  // at the end of a group.1687  const char *args1[] = {"prog", "-fv", "val1"};1688  EXPECT_TRUE(1689      cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));1690  EXPECT_TRUE(OptF);1691  EXPECT_STREQ("val1", OptV.c_str());1692  OptV.clear();1693  cl::ResetAllOptionOccurrences();1694 1695  // Should not crash if it is accidentally used elsewhere in the group.1696  const char *args2[] = {"prog", "-vf", "val2"};1697  EXPECT_FALSE(1698      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));1699  OptV.clear();1700  cl::ResetAllOptionOccurrences();1701 1702  // Should allow the "opt=value" form at the end of the group1703  const char *args3[] = {"prog", "-fv=val3"};1704  EXPECT_TRUE(1705      cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));1706  EXPECT_TRUE(OptF);1707  EXPECT_STREQ("val3", OptV.c_str());1708  OptV.clear();1709  cl::ResetAllOptionOccurrences();1710 1711  // Should allow assigning a value for a ValueOptional option1712  // at the end of the group1713  const char *args4[] = {"prog", "-fo=val4"};1714  EXPECT_TRUE(1715      cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));1716  EXPECT_TRUE(OptF);1717  EXPECT_STREQ("val4", OptO.c_str());1718  OptO.clear();1719  cl::ResetAllOptionOccurrences();1720 1721  // Should assign an empty value if a ValueOptional option is used elsewhere1722  // in the group.1723  const char *args5[] = {"prog", "-fob"};1724  EXPECT_TRUE(1725      cl::ParseCommandLineOptions(2, args5, StringRef(), &llvm::nulls()));1726  EXPECT_TRUE(OptF);1727  EXPECT_EQ(1, OptO.getNumOccurrences());1728  EXPECT_EQ(1, OptB.getNumOccurrences());1729  EXPECT_TRUE(OptO.empty());1730  cl::ResetAllOptionOccurrences();1731 1732  // Should not allow an assignment for a ValueDisallowed option.1733  const char *args6[] = {"prog", "-fd=false"};1734  EXPECT_FALSE(1735      cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));1736}1737 1738TEST(CommandLineTest, GroupingAndPrefix) {1739  cl::ResetCommandLineParser();1740 1741  StackOption<bool> OptF("f", cl::Grouping, cl::desc("Some flag"));1742  StackOption<bool> OptB("b", cl::Grouping, cl::desc("Another flag"));1743  StackOption<std::string> OptP("p", cl::Prefix, cl::Grouping,1744                                cl::desc("Prefix and Grouping"));1745  StackOption<std::string> OptA("a", cl::AlwaysPrefix, cl::Grouping,1746                                cl::desc("AlwaysPrefix and Grouping"));1747 1748  // Should be possible to use a cl::Prefix option without grouping.1749  const char *args1[] = {"prog", "-pval1"};1750  EXPECT_TRUE(1751      cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls()));1752  EXPECT_STREQ("val1", OptP.c_str());1753  OptP.clear();1754  cl::ResetAllOptionOccurrences();1755 1756  // Should be possible to pass a value in a separate argument.1757  const char *args2[] = {"prog", "-p", "val2"};1758  EXPECT_TRUE(1759      cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));1760  EXPECT_STREQ("val2", OptP.c_str());1761  OptP.clear();1762  cl::ResetAllOptionOccurrences();1763 1764  // The "-opt=value" form should work, too.1765  const char *args3[] = {"prog", "-p=val3"};1766  EXPECT_TRUE(1767      cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));1768  EXPECT_STREQ("val3", OptP.c_str());1769  OptP.clear();1770  cl::ResetAllOptionOccurrences();1771 1772  // All three previous cases should work the same way if an option with both1773  // cl::Prefix and cl::Grouping modifiers is used at the end of a group.1774  const char *args4[] = {"prog", "-fpval4"};1775  EXPECT_TRUE(1776      cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));1777  EXPECT_TRUE(OptF);1778  EXPECT_STREQ("val4", OptP.c_str());1779  OptP.clear();1780  cl::ResetAllOptionOccurrences();1781 1782  const char *args5[] = {"prog", "-fp", "val5"};1783  EXPECT_TRUE(1784      cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls()));1785  EXPECT_TRUE(OptF);1786  EXPECT_STREQ("val5", OptP.c_str());1787  OptP.clear();1788  cl::ResetAllOptionOccurrences();1789 1790  const char *args6[] = {"prog", "-fp=val6"};1791  EXPECT_TRUE(1792      cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));1793  EXPECT_TRUE(OptF);1794  EXPECT_STREQ("val6", OptP.c_str());1795  OptP.clear();1796  cl::ResetAllOptionOccurrences();1797 1798  // Should assign a value even if the part after a cl::Prefix option is equal1799  // to the name of another option.1800  const char *args7[] = {"prog", "-fpb"};1801  EXPECT_TRUE(1802      cl::ParseCommandLineOptions(2, args7, StringRef(), &llvm::nulls()));1803  EXPECT_TRUE(OptF);1804  EXPECT_STREQ("b", OptP.c_str());1805  EXPECT_FALSE(OptB);1806  OptP.clear();1807  cl::ResetAllOptionOccurrences();1808 1809  // Should be possible to use a cl::AlwaysPrefix option without grouping.1810  const char *args8[] = {"prog", "-aval8"};1811  EXPECT_TRUE(1812      cl::ParseCommandLineOptions(2, args8, StringRef(), &llvm::nulls()));1813  EXPECT_STREQ("val8", OptA.c_str());1814  OptA.clear();1815  cl::ResetAllOptionOccurrences();1816 1817  // Should not be possible to pass a value in a separate argument.1818  const char *args9[] = {"prog", "-a", "val9"};1819  EXPECT_FALSE(1820      cl::ParseCommandLineOptions(3, args9, StringRef(), &llvm::nulls()));1821  cl::ResetAllOptionOccurrences();1822 1823  // With the "-opt=value" form, the "=" symbol should be preserved.1824  const char *args10[] = {"prog", "-a=val10"};1825  EXPECT_TRUE(1826      cl::ParseCommandLineOptions(2, args10, StringRef(), &llvm::nulls()));1827  EXPECT_STREQ("=val10", OptA.c_str());1828  OptA.clear();1829  cl::ResetAllOptionOccurrences();1830 1831  // All three previous cases should work the same way if an option with both1832  // cl::AlwaysPrefix and cl::Grouping modifiers is used at the end of a group.1833  const char *args11[] = {"prog", "-faval11"};1834  EXPECT_TRUE(1835      cl::ParseCommandLineOptions(2, args11, StringRef(), &llvm::nulls()));1836  EXPECT_TRUE(OptF);1837  EXPECT_STREQ("val11", OptA.c_str());1838  OptA.clear();1839  cl::ResetAllOptionOccurrences();1840 1841  const char *args12[] = {"prog", "-fa", "val12"};1842  EXPECT_FALSE(1843      cl::ParseCommandLineOptions(3, args12, StringRef(), &llvm::nulls()));1844  cl::ResetAllOptionOccurrences();1845 1846  const char *args13[] = {"prog", "-fa=val13"};1847  EXPECT_TRUE(1848      cl::ParseCommandLineOptions(2, args13, StringRef(), &llvm::nulls()));1849  EXPECT_TRUE(OptF);1850  EXPECT_STREQ("=val13", OptA.c_str());1851  OptA.clear();1852  cl::ResetAllOptionOccurrences();1853 1854  // Should assign a value even if the part after a cl::AlwaysPrefix option1855  // is equal to the name of another option.1856  const char *args14[] = {"prog", "-fab"};1857  EXPECT_TRUE(1858      cl::ParseCommandLineOptions(2, args14, StringRef(), &llvm::nulls()));1859  EXPECT_TRUE(OptF);1860  EXPECT_STREQ("b", OptA.c_str());1861  EXPECT_FALSE(OptB);1862  OptA.clear();1863  cl::ResetAllOptionOccurrences();1864}1865 1866TEST(CommandLineTest, LongOptions) {1867  cl::ResetCommandLineParser();1868 1869  StackOption<bool> OptA("a", cl::desc("Some flag"));1870  StackOption<bool> OptBLong("long-flag", cl::desc("Some long flag"));1871  StackOption<bool, cl::alias> OptB("b", cl::desc("Alias to --long-flag"),1872                                    cl::aliasopt(OptBLong));1873  StackOption<std::string> OptAB("ab", cl::desc("Another long option"));1874 1875  std::string Errs;1876  raw_string_ostream OS(Errs);1877 1878  const char *args1[] = {"prog", "-a", "-ab", "val1"};1879  const char *args2[] = {"prog", "-a", "--ab", "val1"};1880  const char *args3[] = {"prog", "-ab", "--ab", "val1"};1881 1882  //1883  // The following tests treat `-` and `--` the same, and always match the1884  // longest string.1885  //1886 1887  EXPECT_TRUE(1888      cl::ParseCommandLineOptions(4, args1, StringRef(), &OS));1889  EXPECT_TRUE(OptA);1890  EXPECT_FALSE(OptBLong);1891  EXPECT_STREQ("val1", OptAB.c_str());1892  EXPECT_TRUE(Errs.empty()); Errs.clear();1893  cl::ResetAllOptionOccurrences();1894 1895  EXPECT_TRUE(1896      cl::ParseCommandLineOptions(4, args2, StringRef(), &OS));1897  EXPECT_TRUE(OptA);1898  EXPECT_FALSE(OptBLong);1899  EXPECT_STREQ("val1", OptAB.c_str());1900  EXPECT_TRUE(Errs.empty()); Errs.clear();1901  cl::ResetAllOptionOccurrences();1902 1903  // Fails because `-ab` and `--ab` are treated the same and appear more than1904  // once.  Also, `val1` is unexpected.1905  EXPECT_FALSE(1906      cl::ParseCommandLineOptions(4, args3, StringRef(), &OS));1907  outs()<< Errs << "\n";1908  EXPECT_FALSE(Errs.empty()); Errs.clear();1909  cl::ResetAllOptionOccurrences();1910 1911  //1912  // The following tests treat `-` and `--` differently, with `-` for short, and1913  // `--` for long options.1914  //1915 1916  // Fails because `-ab` is treated as `-a -b`, so `-a` is seen twice, and1917  // `val1` is unexpected.1918  EXPECT_FALSE(cl::ParseCommandLineOptions(4, args1, StringRef(), &OS, nullptr,1919                                           nullptr, true));1920  EXPECT_FALSE(Errs.empty()); Errs.clear();1921  cl::ResetAllOptionOccurrences();1922 1923  // Works because `-a` is treated differently than `--ab`.1924  EXPECT_TRUE(cl::ParseCommandLineOptions(4, args2, StringRef(), &OS, nullptr,1925                                          nullptr, true));1926  EXPECT_TRUE(Errs.empty()); Errs.clear();1927  cl::ResetAllOptionOccurrences();1928 1929  // Works because `-ab` is treated as `-a -b`, and `--ab` is a long option.1930  EXPECT_TRUE(cl::ParseCommandLineOptions(4, args3, StringRef(), &OS, nullptr,1931                                          nullptr, true));1932  EXPECT_TRUE(OptA);1933  EXPECT_TRUE(OptBLong);1934  EXPECT_STREQ("val1", OptAB.c_str());1935  EXPECT_TRUE(Errs.empty()); Errs.clear();1936  cl::ResetAllOptionOccurrences();1937}1938 1939TEST(CommandLineTest, OptionErrorMessage) {1940  // When there is an error, we expect some error message like:1941  //   prog: for the -a option: [...]1942  //1943  // Test whether the "for the -a option"-part is correctly formatted.1944  cl::ResetCommandLineParser();1945 1946  StackOption<bool> OptA("a", cl::desc("Some option"));1947  StackOption<bool> OptLong("long", cl::desc("Some long option"));1948 1949  std::string Errs;1950  raw_string_ostream OS(Errs);1951 1952  OptA.error("custom error", OS);1953  EXPECT_NE(Errs.find("for the -a option:"), std::string::npos);1954  Errs.clear();1955 1956  OptLong.error("custom error", OS);1957  EXPECT_NE(Errs.find("for the --long option:"), std::string::npos);1958  Errs.clear();1959 1960  cl::ResetAllOptionOccurrences();1961}1962 1963TEST(CommandLineTest, OptionErrorMessageSuggest) {1964  // When there is an error, and the edit-distance is not very large,1965  // we expect some error message like:1966  //   prog: did you mean '--option'?1967  //1968  // Test whether this message is well-formatted.1969  cl::ResetCommandLineParser();1970 1971  StackOption<bool> OptLong("aluminium", cl::desc("Some long option"));1972 1973  const char *args[] = {"prog", "--aluminum"};1974 1975  std::string Errs;1976  raw_string_ostream OS(Errs);1977 1978  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));1979  EXPECT_NE(Errs.find("prog: Did you mean '--aluminium'?\n"),1980            std::string::npos);1981  Errs.clear();1982 1983  cl::ResetAllOptionOccurrences();1984}1985 1986TEST(CommandLineTest, OptionErrorMessageSuggestNoHidden) {1987  // We expect that 'really hidden' option do not show up in option1988  // suggestions.1989  cl::ResetCommandLineParser();1990 1991  StackOption<bool> OptLong("aluminium", cl::desc("Some long option"));1992  StackOption<bool> OptLong2("aluminum", cl::desc("Bad option"),1993                             cl::ReallyHidden);1994 1995  const char *args[] = {"prog", "--alumnum"};1996 1997  std::string Errs;1998  raw_string_ostream OS(Errs);1999 2000  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));2001  EXPECT_NE(Errs.find("prog: Did you mean '--aluminium'?\n"),2002            std::string::npos);2003  Errs.clear();2004 2005  cl::ResetAllOptionOccurrences();2006}2007 2008TEST(CommandLineTest, Callback) {2009  cl::ResetCommandLineParser();2010 2011  StackOption<bool> OptA("a", cl::desc("option a"));2012  StackOption<bool> OptB(2013      "b", cl::desc("option b -- This option turns on option a"),2014      cl::callback([&](const bool &) { OptA = true; }));2015  StackOption<bool> OptC(2016      "c", cl::desc("option c -- This option turns on options a and b"),2017      cl::callback([&](const bool &) { OptB = true; }));2018  StackOption<std::string, cl::list<std::string>> List(2019      "list",2020      cl::desc("option list -- This option turns on options a, b, and c when "2021               "'foo' is included in list"),2022      cl::CommaSeparated,2023      cl::callback([&](const std::string &Str) {2024        if (Str == "foo")2025          OptC = true;2026      }));2027 2028  const char *args1[] = {"prog", "-a"};2029  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1));2030  EXPECT_TRUE(OptA);2031  EXPECT_FALSE(OptB);2032  EXPECT_FALSE(OptC);2033  EXPECT_EQ(List.size(), 0u);2034  cl::ResetAllOptionOccurrences();2035 2036  const char *args2[] = {"prog", "-b"};2037  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args2));2038  EXPECT_TRUE(OptA);2039  EXPECT_TRUE(OptB);2040  EXPECT_FALSE(OptC);2041  EXPECT_EQ(List.size(), 0u);2042  cl::ResetAllOptionOccurrences();2043 2044  const char *args3[] = {"prog", "-c"};2045  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3));2046  EXPECT_TRUE(OptA);2047  EXPECT_TRUE(OptB);2048  EXPECT_TRUE(OptC);2049  EXPECT_EQ(List.size(), 0u);2050  cl::ResetAllOptionOccurrences();2051 2052  const char *args4[] = {"prog", "--list=foo,bar"};2053  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4));2054  EXPECT_TRUE(OptA);2055  EXPECT_TRUE(OptB);2056  EXPECT_TRUE(OptC);2057  EXPECT_EQ(List.size(), 2u);2058  cl::ResetAllOptionOccurrences();2059 2060  const char *args5[] = {"prog", "--list=bar"};2061  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5));2062  EXPECT_FALSE(OptA);2063  EXPECT_FALSE(OptB);2064  EXPECT_FALSE(OptC);2065  EXPECT_EQ(List.size(), 1u);2066 2067  cl::ResetAllOptionOccurrences();2068}2069 2070enum Enum { Val1, Val2 };2071static cl::bits<Enum> ExampleBits(2072    cl::desc("An example cl::bits to ensure it compiles"),2073    cl::values(2074      clEnumValN(Val1, "bits-val1", "The Val1 value"),2075      clEnumValN(Val1, "bits-val2", "The Val2 value")));2076 2077TEST(CommandLineTest, ConsumeAfterOnePositional) {2078  cl::ResetCommandLineParser();2079 2080  // input [args]2081  StackOption<std::string, cl::opt<std::string>> Input(cl::Positional,2082                                                       cl::Required);2083  StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);2084 2085  const char *Args[] = {"prog", "input", "arg1", "arg2"};2086 2087  std::string Errs;2088  raw_string_ostream OS(Errs);2089  EXPECT_TRUE(cl::ParseCommandLineOptions(4, Args, StringRef(), &OS));2090  EXPECT_EQ("input", Input);2091  EXPECT_EQ(ExtraArgs.size(), 2u);2092  EXPECT_EQ(ExtraArgs[0], "arg1");2093  EXPECT_EQ(ExtraArgs[1], "arg2");2094  EXPECT_TRUE(Errs.empty());2095}2096 2097TEST(CommandLineTest, ConsumeAfterTwoPositionals) {2098  cl::ResetCommandLineParser();2099 2100  // input1 input2 [args]2101  StackOption<std::string, cl::opt<std::string>> Input1(cl::Positional,2102                                                        cl::Required);2103  StackOption<std::string, cl::opt<std::string>> Input2(cl::Positional,2104                                                        cl::Required);2105  StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);2106 2107  const char *Args[] = {"prog", "input1", "input2", "arg1", "arg2"};2108 2109  std::string Errs;2110  raw_string_ostream OS(Errs);2111  EXPECT_TRUE(cl::ParseCommandLineOptions(5, Args, StringRef(), &OS));2112  EXPECT_EQ("input1", Input1);2113  EXPECT_EQ("input2", Input2);2114  EXPECT_EQ(ExtraArgs.size(), 2u);2115  EXPECT_EQ(ExtraArgs[0], "arg1");2116  EXPECT_EQ(ExtraArgs[1], "arg2");2117  EXPECT_TRUE(Errs.empty());2118}2119 2120TEST(CommandLineTest, ConsumeOptionalString) {2121  cl::ResetCommandLineParser();2122 2123  StackOption<std::optional<std::string>, cl::opt<std::optional<std::string>>>2124      Input("input");2125 2126  const char *Args[] = {"prog", "--input=\"value\""};2127 2128  std::string Errs;2129  raw_string_ostream OS(Errs);2130  ASSERT_TRUE(cl::ParseCommandLineOptions(2, Args, StringRef(), &OS));2131  ASSERT_TRUE(Input.has_value());2132  EXPECT_EQ("\"value\"", *Input);2133  EXPECT_TRUE(Errs.empty());2134}2135 2136TEST(CommandLineTest, ResetAllOptionOccurrences) {2137  cl::ResetCommandLineParser();2138 2139  // -option -str -enableA -enableC [sink] input [args]2140  StackOption<bool> Option("option");2141  StackOption<std::string> Str("str");2142  enum Vals { ValA, ValB, ValC };2143  StackOption<Vals, cl::bits<Vals>> Bits(2144      cl::values(clEnumValN(ValA, "enableA", "Enable A"),2145                 clEnumValN(ValB, "enableB", "Enable B"),2146                 clEnumValN(ValC, "enableC", "Enable C")));2147  StackOption<std::string, cl::list<std::string>> Sink(cl::Sink);2148  StackOption<std::string> Input(cl::Positional);2149  StackOption<std::string, cl::list<std::string>> ExtraArgs(cl::ConsumeAfter);2150 2151  const char *Args[] = {"prog",     "-option",  "-str=STR", "-enableA",2152                        "-enableC", "-unknown", "input",    "-arg"};2153 2154  std::string Errs;2155  raw_string_ostream OS(Errs);2156  EXPECT_TRUE(cl::ParseCommandLineOptions(8, Args, StringRef(), &OS));2157  EXPECT_TRUE(OS.str().empty());2158 2159  EXPECT_TRUE(Option);2160  EXPECT_EQ("STR", Str);2161  EXPECT_EQ((1u << ValA) | (1u << ValC), Bits.getBits());2162  EXPECT_EQ(1u, Sink.size());2163  EXPECT_EQ("-unknown", Sink[0]);2164  EXPECT_EQ("input", Input);2165  EXPECT_EQ(1u, ExtraArgs.size());2166  EXPECT_EQ("-arg", ExtraArgs[0]);2167 2168  cl::ResetAllOptionOccurrences();2169  EXPECT_FALSE(Option);2170  EXPECT_EQ("", Str);2171  EXPECT_EQ(0u, Bits.getBits());2172  EXPECT_EQ(0u, Sink.size());2173  EXPECT_EQ(0, Input.getNumOccurrences());2174  EXPECT_EQ(0u, ExtraArgs.size());2175}2176 2177TEST(CommandLineTest, DefaultValue) {2178  cl::ResetCommandLineParser();2179 2180  StackOption<bool> BoolOption("bool-option");2181  StackOption<std::string> StrOption("str-option");2182  StackOption<bool> BoolInitOption("bool-init-option", cl::init(true));2183  StackOption<std::string> StrInitOption("str-init-option",2184                                         cl::init("str-default-value"));2185 2186  const char *Args[] = {"prog"}; // no options2187 2188  std::string Errs;2189  raw_string_ostream OS(Errs);2190  EXPECT_TRUE(cl::ParseCommandLineOptions(1, Args, StringRef(), &OS));2191  EXPECT_TRUE(OS.str().empty());2192 2193  EXPECT_TRUE(!BoolOption);2194  EXPECT_FALSE(BoolOption.Default.hasValue());2195  EXPECT_EQ(0, BoolOption.getNumOccurrences());2196 2197  EXPECT_EQ("", StrOption);2198  EXPECT_FALSE(StrOption.Default.hasValue());2199  EXPECT_EQ(0, StrOption.getNumOccurrences());2200 2201  EXPECT_TRUE(BoolInitOption);2202  EXPECT_TRUE(BoolInitOption.Default.hasValue());2203  EXPECT_EQ(0, BoolInitOption.getNumOccurrences());2204 2205  EXPECT_EQ("str-default-value", StrInitOption);2206  EXPECT_TRUE(StrInitOption.Default.hasValue());2207  EXPECT_EQ(0, StrInitOption.getNumOccurrences());2208 2209  const char *Args2[] = {"prog", "-bool-option", "-str-option=str-value",2210                         "-bool-init-option=0",2211                         "-str-init-option=str-init-value"};2212 2213  EXPECT_TRUE(cl::ParseCommandLineOptions(5, Args2, StringRef(), &OS));2214  EXPECT_TRUE(OS.str().empty());2215 2216  EXPECT_TRUE(BoolOption);2217  EXPECT_FALSE(BoolOption.Default.hasValue());2218  EXPECT_EQ(1, BoolOption.getNumOccurrences());2219 2220  EXPECT_EQ("str-value", StrOption);2221  EXPECT_FALSE(StrOption.Default.hasValue());2222  EXPECT_EQ(1, StrOption.getNumOccurrences());2223 2224  EXPECT_FALSE(BoolInitOption);2225  EXPECT_TRUE(BoolInitOption.Default.hasValue());2226  EXPECT_EQ(1, BoolInitOption.getNumOccurrences());2227 2228  EXPECT_EQ("str-init-value", StrInitOption);2229  EXPECT_TRUE(StrInitOption.Default.hasValue());2230  EXPECT_EQ(1, StrInitOption.getNumOccurrences());2231}2232 2233TEST(CommandLineTest, HelpWithoutSubcommands) {2234  // Check that the help message does not contain the "[subcommand]" placeholder2235  // and the "SUBCOMMANDS" section if there are no subcommands.2236  cl::ResetCommandLineParser();2237  StackOption<bool> Opt("opt", cl::init(false));2238  const char *args[] = {"prog"};2239  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),2240                                          &llvm::nulls()));2241  auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });2242  EXPECT_NE(std::string::npos, Output.find("USAGE: prog [options]")) << Output;2243  EXPECT_EQ(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;2244  cl::ResetCommandLineParser();2245}2246 2247TEST(CommandLineTest, HelpWithSubcommands) {2248  // Check that the help message contains the "[subcommand]" placeholder in the2249  // "USAGE" line and describes subcommands.2250  cl::ResetCommandLineParser();2251  StackSubCommand SC1("sc1", "First Subcommand");2252  StackSubCommand SC2("sc2", "Second Subcommand");2253  StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));2254  StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));2255  const char *args[] = {"prog"};2256  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),2257                                          &llvm::nulls()));2258  auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });2259  EXPECT_NE(std::string::npos,2260            Output.find("USAGE: prog [subcommand] [options]"))2261      << Output;2262  EXPECT_NE(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;2263  EXPECT_NE(std::string::npos, Output.find("sc1 - First Subcommand")) << Output;2264  EXPECT_NE(std::string::npos, Output.find("sc2 - Second Subcommand"))2265      << Output;2266  cl::ResetCommandLineParser();2267}2268 2269TEST(CommandLineTest, UnknownCommands) {2270  cl::ResetCommandLineParser();2271 2272  StackSubCommand SC1("foo", "Foo subcommand");2273  StackSubCommand SC2("bar", "Bar subcommand");2274  StackOption<bool> SC1Opt("put", cl::sub(SC1));2275  StackOption<bool> SC2Opt("get", cl::sub(SC2));2276  StackOption<bool> TopOpt1("peek");2277  StackOption<bool> TopOpt2("set");2278 2279  std::string Errs;2280  raw_string_ostream OS(Errs);2281 2282  const char *Args1[] = {"prog", "baz", "--get"};2283  EXPECT_FALSE(2284      cl::ParseCommandLineOptions(std::size(Args1), Args1, StringRef(), &OS));2285  EXPECT_EQ(Errs,2286            "prog: Unknown subcommand 'baz'.  Try: 'prog --help'\n"2287            "prog: Did you mean 'bar'?\n"2288            "prog: Unknown command line argument '--get'.  Try: 'prog --help'\n"2289            "prog: Did you mean '--set'?\n");2290 2291  // Do not show a suggestion if the subcommand is not similar to any known.2292  Errs.clear();2293  const char *Args2[] = {"prog", "faz"};2294  EXPECT_FALSE(2295      cl::ParseCommandLineOptions(std::size(Args2), Args2, StringRef(), &OS));2296  EXPECT_EQ(Errs, "prog: Unknown subcommand 'faz'.  Try: 'prog --help'\n");2297}2298 2299TEST(CommandLineTest, SubCommandGroups) {2300  // Check that options in subcommand groups are associated with expected2301  // subcommands.2302 2303  cl::ResetCommandLineParser();2304 2305  StackSubCommand SC1("sc1", "SC1 subcommand");2306  StackSubCommand SC2("sc2", "SC2 subcommand");2307  StackSubCommand SC3("sc3", "SC3 subcommand");2308  cl::SubCommandGroup Group12 = {&SC1, &SC2};2309 2310  StackOption<bool> Opt12("opt12", cl::sub(Group12), cl::init(false));2311  StackOption<bool> Opt3("opt3", cl::sub(SC3), cl::init(false));2312 2313  // The "--opt12" option is expected to be added to both subcommands in the2314  // group, but not to the top-level "no subcommand" pseudo-subcommand or the2315  // "sc3" subcommand.2316  EXPECT_EQ(1U, SC1.OptionsMap.size());2317  EXPECT_TRUE(SC1.OptionsMap.contains("opt12"));2318 2319  EXPECT_EQ(1U, SC2.OptionsMap.size());2320  EXPECT_TRUE(SC2.OptionsMap.contains("opt12"));2321 2322  EXPECT_FALSE(cl::SubCommand::getTopLevel().OptionsMap.contains("opt12"));2323  EXPECT_FALSE(SC3.OptionsMap.contains("opt12"));2324}2325 2326TEST(CommandLineTest, HelpWithEmptyCategory) {2327  cl::ResetCommandLineParser();2328 2329  cl::OptionCategory Category1("First Category");2330  cl::OptionCategory Category2("Second Category");2331  StackOption<int> Opt1("opt1", cl::cat(Category1));2332  StackOption<int> Opt2("opt2", cl::cat(Category2));2333  cl::HideUnrelatedOptions(Category2);2334 2335  const char *args[] = {"prog"};2336  EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),2337                                          &llvm::nulls()));2338  auto Output = interceptStdout(2339      []() { cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true); });2340  EXPECT_EQ(std::string::npos, Output.find("First Category"))2341      << "An empty category should not be printed";2342 2343  Output = interceptStdout(2344      []() { cl::PrintHelpMessage(/*Hidden=*/true, /*Categorized=*/true); });2345  EXPECT_EQ(std::string::npos, Output.find("First Category"))2346      << "An empty category should not be printed";2347 2348  cl::ResetCommandLineParser();2349}2350 2351} // anonymous namespace2352