Index: include/llvm/Support/CommandLine.h =================================================================== --- include/llvm/Support/CommandLine.h +++ include/llvm/Support/CommandLine.h @@ -50,9 +50,12 @@ //===----------------------------------------------------------------------===// // ParseCommandLineOptions - Command line option processing entry point. // +// Returns true on success. Otherwise, this will print the error message to +// stderr and exit if \p Errs is not set (nullptr by default), or print the +// error message to \p Errs and return false if \p Errs is provided. bool ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview = "", - bool IgnoreErrors = false); + raw_ostream *Errs = nullptr); //===----------------------------------------------------------------------===// // ParseEnvironmentOptions - Environment variable option processing alternate Index: lib/Support/CommandLine.cpp =================================================================== --- lib/Support/CommandLine.cpp +++ lib/Support/CommandLine.cpp @@ -123,7 +123,7 @@ void ResetAllOptionOccurrences(); bool ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, bool IgnoreErrors); + StringRef Overview, raw_ostream *Errs = nullptr); void addLiteralOption(Option &Opt, SubCommand *SC, StringRef Name) { if (Opt.hasArgStr()) @@ -1013,9 +1013,9 @@ } bool cl::ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, bool IgnoreErrors) { + StringRef Overview, raw_ostream *Errs) { return GlobalParser->ParseCommandLineOptions(argc, argv, Overview, - IgnoreErrors); + Errs); } void CommandLineParser::ResetAllOptionOccurrences() { @@ -1030,7 +1030,7 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, - bool IgnoreErrors) { + raw_ostream *Errs) { assert(hasOptions() && "No options specified!"); // Expand response files. @@ -1045,6 +1045,9 @@ ProgramName = sys::path::filename(StringRef(argv[0])); ProgramOverview = Overview; + bool IgnoreErrors = Errs; + if (!Errs) + Errs = &errs(); bool ErrorParsing = false; // Check out the positional arguments to collect information about them. @@ -1097,15 +1100,14 @@ // not specified after an option that eats all extra arguments, or this // one will never get any! // - if (!IgnoreErrors) { + if (!IgnoreErrors) Opt->error("error - option can never match, because " "another positional argument will match an " "unbounded number of values, and this option" " does not require a value!"); - errs() << ProgramName << ": CommandLine Error: Option '" - << Opt->ArgStr << "' is all messed up!\n"; - errs() << PositionalOpts.size(); - } + *Errs << ProgramName << ": CommandLine Error: Option '" << Opt->ArgStr + << "' is all messed up!\n"; + *Errs << PositionalOpts.size(); ErrorParsing = true; } UnboundedFound |= EatsUnboundedNumberOfValues(Opt); @@ -1200,15 +1202,13 @@ if (!Handler) { if (SinkOpts.empty()) { - if (!IgnoreErrors) { - errs() << ProgramName << ": Unknown command line argument '" - << argv[i] << "'. Try: '" << argv[0] << " -help'\n"; - - if (NearestHandler) { - // If we know a near match, report it as well. - errs() << ProgramName << ": Did you mean '-" << NearestHandlerString - << "'?\n"; - } + *Errs << ProgramName << ": Unknown command line argument '" << argv[i] + << "'. Try: '" << argv[0] << " -help'\n"; + + if (NearestHandler) { + // If we know a near match, report it as well. + *Errs << ProgramName << ": Did you mean '-" << NearestHandlerString + << "'?\n"; } ErrorParsing = true; @@ -1231,22 +1231,18 @@ // Check and handle positional arguments now... if (NumPositionalRequired > PositionalVals.size()) { - if (!IgnoreErrors) { - errs() << ProgramName + *Errs << ProgramName << ": Not enough positional command line arguments specified!\n" << "Must specify at least " << NumPositionalRequired << " positional argument" << (NumPositionalRequired > 1 ? "s" : "") << ": See: " << argv[0] << " - help\n"; - } ErrorParsing = true; } else if (!HasUnlimitedPositionals && PositionalVals.size() > PositionalOpts.size()) { - if (!IgnoreErrors) { - errs() << ProgramName << ": Too many positional arguments specified!\n" - << "Can specify at most " << PositionalOpts.size() - << " positional arguments: See: " << argv[0] << " -help\n"; - } + *Errs << ProgramName << ": Too many positional arguments specified!\n" + << "Can specify at most " << PositionalOpts.size() + << " positional arguments: See: " << argv[0] << " -help\n"; ErrorParsing = true; } else if (!ConsumeAfterOpt) { @@ -2182,5 +2178,6 @@ void LLVMParseCommandLineOptions(int argc, const char *const *argv, const char *Overview) { - llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), true); + llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), + &llvm::nulls()); } Index: unittests/Support/CommandLineTest.cpp =================================================================== --- unittests/Support/CommandLineTest.cpp +++ unittests/Support/CommandLineTest.cpp @@ -303,7 +303,8 @@ EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args[] = {"prog", "-top-level"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -315,7 +316,8 @@ EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args2[] = {"prog", "sc1", "-sc1"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); EXPECT_FALSE(TopLevelOpt); EXPECT_TRUE(SC1Opt); EXPECT_FALSE(SC2Opt); @@ -327,7 +329,8 @@ EXPECT_FALSE(SC1Opt); EXPECT_FALSE(SC2Opt); const char *args3[] = {"prog", "sc2", "-sc2"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls())); EXPECT_FALSE(TopLevelOpt); EXPECT_FALSE(SC1Opt); EXPECT_TRUE(SC2Opt); @@ -342,8 +345,13 @@ StackOption SC1Opt("sc1", cl::sub(SC1), cl::init(false)); StackOption SC2Opt("sc2", cl::sub(SC2), cl::init(false)); + std::string Errs; + raw_string_ostream OS(Errs); + const char *args[] = {"prog", "sc1", "-sc2"}; - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); + OS.flush(); + EXPECT_FALSE(Errs.empty()); } TEST(CommandLineTest, AddToAllSubCommands) { @@ -358,23 +366,30 @@ const char *args2[] = {"prog", "sc1", "-everywhere"}; const char *args3[] = {"prog", "sc2", "-everywhere"}; + std::string Errs; + raw_string_ostream OS(Errs); + EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); EXPECT_TRUE(AllOpt); AllOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(AllOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); EXPECT_TRUE(AllOpt); + + // Since all parsing succeeded, the error message should be empty. + OS.flush(); + EXPECT_TRUE(Errs.empty()); } TEST(CommandLineTest, ReparseCommandLineOptions) { @@ -386,14 +401,16 @@ const char *args[] = {"prog", "-top-level"}; EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); TopLevelOpt = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(TopLevelOpt); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelOpt); } @@ -406,14 +423,21 @@ const char *args[] = {"prog", "sc", "-remove-option"}; + std::string Errs; + raw_string_ostream OS(Errs); + EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); EXPECT_TRUE(RemoveOption); + OS.flush(); + EXPECT_TRUE(Errs.empty()); RemoveOption.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), true)); + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS)); + OS.flush(); + EXPECT_FALSE(Errs.empty()); } TEST(CommandLineTest, RemoveFromTopLevelSubCommand) { @@ -427,13 +451,15 @@ const char *args[] = {"prog", "-top-level-remove"}; EXPECT_FALSE(TopLevelRemove); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); EXPECT_TRUE(TopLevelRemove); TopLevelRemove.removeArgument(); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls())); } TEST(CommandLineTest, RemoveFromAllSubCommands) { @@ -452,32 +478,38 @@ // It should work for all subcommands including the top-level. EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption = false; cl::ResetAllOptionOccurrences(); EXPECT_FALSE(RemoveOption); - EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); EXPECT_TRUE(RemoveOption); RemoveOption.removeArgument(); // It should not work for any subcommands including the top-level. cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls())); cl::ResetAllOptionOccurrences(); - EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), true)); + EXPECT_FALSE( + cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls())); } TEST(CommandLineTest, GetRegisteredSubcommands) { @@ -491,7 +523,8 @@ const char *args0[] = {"prog", "sc1"}; const char *args1[] = {"prog", "sc2"}; - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls())); EXPECT_FALSE(Opt1); EXPECT_FALSE(Opt2); for (auto *S : cl::getRegisteredSubcommands()) { @@ -500,7 +533,8 @@ } cl::ResetAllOptionOccurrences(); - EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1, StringRef(), true)); + EXPECT_TRUE( + cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls())); EXPECT_FALSE(Opt1); EXPECT_FALSE(Opt2); for (auto *S : cl::getRegisteredSubcommands()) {