Refactor command line errors to mirror diagnostic style (#4568)

This changes to an `Error` return to let the driver do the "error: "
prefix, except for one case with `help` that needs more work to change
(I'm not planning on picking up that TODO). It also changes
capitalization, backtick use, and a few minor punctuation things to try
to better match the diagnostic style.

This also adds `Error` matchers so that the changes to command line
testing are clearer.
This commit is contained in:
Jon Ross-Perkins
2024-11-26 19:22:05 +00:00
committed by GitHub
parent f921923b4b
commit 5880954041
11 changed files with 475 additions and 372 deletions
+157 -180
View File
@@ -15,8 +15,6 @@ namespace Carbon::CommandLine {
auto operator<<(llvm::raw_ostream& output, ParseResult result)
-> llvm::raw_ostream& {
switch (result) {
case ParseResult::Error:
return output << "Error";
case ParseResult::MetaSuccess:
return output << "MetaSuccess";
case ParseResult::Success:
@@ -311,8 +309,10 @@ void MetaPrinter::PrintHelpForSubcommandName(
}
}
*out_ << "ERROR: Could not find a subcommand named '" << subcommand_name
<< "'.\n";
// TODO: This should really be connected up so that parsing can return an
// Error instead of ParseResult::MetaSuccess in this case.
*out_ << "error: could not find a subcommand named '" << subcommand_name
<< "'\n";
}
void MetaPrinter::PrintVersion(const Command& command) const {
@@ -330,12 +330,12 @@ void MetaPrinter::PrintVersion(const Command& command) const {
void MetaPrinter::PrintSubcommands(const Command& command) const {
for (const auto& subcommand :
llvm::ArrayRef(command.subcommands).drop_back()) {
*out_ << "'" << subcommand->info.name << "', ";
*out_ << "`" << subcommand->info.name << "`, ";
}
if (command.subcommands.size() > 1) {
*out_ << "or ";
}
*out_ << "'" << command.subcommands.back()->info.name << "'";
*out_ << "`" << command.subcommands.back()->info.name << "`";
}
void MetaPrinter::PrintRawVersion(const Command& command,
@@ -632,7 +632,7 @@ void MetaPrinter::PrintUsage(const Command& command) const {
if (!command.parent) {
*out_ << "Usage:\n";
} else {
*out_ << "Subcommand '" << command.info.name << "' usage:\n";
*out_ << "Subcommand `" << command.info.name << "` usage:\n";
}
PrintRawUsage(command, " ");
}
@@ -648,7 +648,7 @@ void MetaPrinter::PrintHelpSubcommands(const Command& command) const {
if (!command.parent) {
*out_ << "\nSubcommands:";
} else {
*out_ << "\nSubcommand '" << command.info.name << "' subcommands:";
*out_ << "\nSubcommand `" << command.info.name << "` subcommands:";
}
}
*out_ << "\n";
@@ -668,8 +668,8 @@ void MetaPrinter::PrintHelpPositionalArgs(const Command& command) const {
if (!command.parent) {
*out_ << "\nPositional arguments:";
} else {
*out_ << "\nSubcommand '" << command.info.name
<< "' positional arguments:";
*out_ << "\nSubcommand `" << command.info.name
<< "` positional arguments:";
}
}
*out_ << "\n";
@@ -692,7 +692,7 @@ void MetaPrinter::PrintHelpOptions(const Command& command) const {
} else if (!command.parent) {
*out_ << "\nCommand options:";
} else {
*out_ << "\nSubcommand '" << command.info.name << "' options:";
*out_ << "\nSubcommand `" << command.info.name << "` options:";
}
}
*out_ << "\n";
@@ -711,12 +711,12 @@ void MetaPrinter::PrintHelpOptions(const Command& command) const {
class Parser {
public:
// `out` and `errors` must not be null.
explicit Parser(llvm::raw_ostream* out, llvm::raw_ostream* errors,
CommandInfo command_info,
// `out` must not be null.
explicit Parser(llvm::raw_ostream* out, CommandInfo command_info,
llvm::function_ref<void(CommandBuilder&)> build);
auto Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args) -> ParseResult;
auto Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
-> ErrorOr<ParseResult>;
private:
friend CommandBuilder;
@@ -739,39 +739,38 @@ class Parser {
void SetOptionDefault(const Arg& option);
auto ParseNegatedFlag(const Arg& flag, std::optional<llvm::StringRef> value)
-> bool;
auto ParseFlag(const Arg& flag, std::optional<llvm::StringRef> value) -> bool;
auto ParseIntegerArgValue(const Arg& arg, llvm::StringRef value) -> bool;
auto ParseStringArgValue(const Arg& arg, llvm::StringRef value) -> bool;
auto ParseOneOfArgValue(const Arg& arg, llvm::StringRef value) -> bool;
-> ErrorOr<Success>;
auto ParseFlag(const Arg& flag, std::optional<llvm::StringRef> value)
-> ErrorOr<Success>;
auto ParseIntegerArgValue(const Arg& arg, llvm::StringRef value)
-> ErrorOr<Success>;
auto ParseStringArgValue(const Arg& arg, llvm::StringRef value)
-> ErrorOr<Success>;
auto ParseOneOfArgValue(const Arg& arg, llvm::StringRef value)
-> ErrorOr<Success>;
auto ParseArg(const Arg& arg, bool short_spelling,
std::optional<llvm::StringRef> value, bool negated_name = false)
-> bool;
-> ErrorOr<Success>;
auto SplitValue(llvm::StringRef& unparsed_arg)
-> std::optional<llvm::StringRef>;
auto ParseLongOption(llvm::StringRef unparsed_arg) -> bool;
auto ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> bool;
auto FinalizeParsedOptions() -> bool;
auto ParseLongOption(llvm::StringRef unparsed_arg) -> ErrorOr<Success>;
auto ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> ErrorOr<Success>;
auto FinalizeParsedOptions() -> ErrorOr<Success>;
auto ParsePositionalArg(llvm::StringRef unparsed_arg) -> bool;
auto ParseSubcommand(llvm::StringRef unparsed_arg) -> bool;
auto ParsePositionalArg(llvm::StringRef unparsed_arg) -> ErrorOr<Success>;
auto ParseSubcommand(llvm::StringRef unparsed_arg) -> ErrorOr<Success>;
auto ParsePositionalSuffix(llvm::ArrayRef<llvm::StringRef> unparsed_args)
-> bool;
-> ErrorOr<Success>;
auto FinalizeParse() -> ParseResult;
auto FinalizeParse() -> ErrorOr<ParseResult>;
// When building a command, it registers arguments and potentially subcommands
// that are meta actions to print things to standard out, so we build a meta
// printer for that here.
MetaPrinter meta_printer_;
// Most parsing output goes to an error stream, and we also provide an
// error-oriented meta printer for when that is useful during parsing.
llvm::raw_ostream* errors_;
MetaPrinter error_meta_printer_;
Command root_command_;
const Command* command_;
@@ -833,89 +832,92 @@ void Parser::SetOptionDefault(const Arg& option) {
}
auto Parser::ParseNegatedFlag(const Arg& flag,
std::optional<llvm::StringRef> value) -> bool {
std::optional<llvm::StringRef> value)
-> ErrorOr<Success> {
if (flag.kind != Arg::Kind::Flag) {
*errors_ << "ERROR: Cannot use a negated flag name by prefixing it with "
"'no-' when it isn't a boolean flag argument.\n";
return false;
return Error(
"cannot use a negated flag name by prefixing it with `no-` when it "
"isn't a boolean flag argument");
}
if (value) {
*errors_ << "ERROR: Cannot specify a value when using a flag name prefixed "
"with 'no-' -- that prefix implies a value of 'false'.\n";
return false;
return Error(
"cannot specify a value when using a flag name prefixed with `no-` -- "
"that prefix implies a value of `false`");
}
*flag.flag_storage = false;
return true;
return Success();
}
auto Parser::ParseFlag(const Arg& flag, std::optional<llvm::StringRef> value)
-> bool {
-> ErrorOr<Success> {
CARBON_CHECK(flag.kind == Arg::Kind::Flag, "Incorrect kind: {0}", flag.kind);
if (!value || *value == "true") {
*flag.flag_storage = true;
} else if (*value == "false") {
*flag.flag_storage = false;
} else {
*errors_ << "ERROR: Invalid value specified for the boolean flag '--"
<< flag.info.name << "': " << *value << "\n";
return false;
return Error(llvm::formatv(
"invalid value specified for the boolean flag `--{0}`: {1}",
flag.info.name, *value));
}
return true;
return Success();
}
auto Parser::ParseIntegerArgValue(const Arg& arg, llvm::StringRef value)
-> bool {
-> ErrorOr<Success> {
CARBON_CHECK(arg.kind == Arg::Kind::Integer, "Incorrect kind: {0}", arg.kind);
int integer_value;
// Note that this method returns *true* on error!
if (value.getAsInteger(/*Radix=*/0, integer_value)) {
*errors_ << "ERROR: Cannot parse value for option '--" << arg.info.name
<< "' as an integer: " << value << "\n";
return false;
return Error(llvm::formatv(
"cannot parse value for option `--{0}` as an integer: {1}",
arg.info.name, value));
}
if (!arg.is_append) {
*arg.integer_storage = integer_value;
} else {
arg.integer_sequence->push_back(integer_value);
}
return true;
return Success();
}
auto Parser::ParseStringArgValue(const Arg& arg, llvm::StringRef value)
-> bool {
-> ErrorOr<Success> {
CARBON_CHECK(arg.kind == Arg::Kind::String, "Incorrect kind: {0}", arg.kind);
if (!arg.is_append) {
*arg.string_storage = value;
} else {
arg.string_sequence->push_back(value);
}
return true;
return Success();
}
auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value) -> bool {
auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value)
-> ErrorOr<Success> {
CARBON_CHECK(arg.kind == Arg::Kind::OneOf, "Incorrect kind: {0}", arg.kind);
if (!arg.value_action(arg, value)) {
*errors_ << "ERROR: Option '--" << arg.info.name << "=";
llvm::printEscapedString(value, *errors_);
*errors_ << "' has an invalid value '";
llvm::printEscapedString(value, *errors_);
*errors_ << "'; valid values are: ";
std::string error_str;
llvm::raw_string_ostream error(error_str);
error << "option `--" << arg.info.name << "=";
llvm::printEscapedString(value, error);
error << "` has an invalid value `";
llvm::printEscapedString(value, error);
error << "`; valid values are: ";
for (auto value_string : arg.value_strings.drop_back()) {
*errors_ << "'" << value_string << "', ";
error << "`" << value_string << "`, ";
}
if (arg.value_strings.size() > 1) {
*errors_ << "or ";
error << "or ";
}
*errors_ << "'" << arg.value_strings.back() << "'\n";
return false;
error << "`" << arg.value_strings.back() << "`";
return Error(error_str);
}
return true;
return Success();
}
auto Parser::ParseArg(const Arg& arg, bool short_spelling,
std::optional<llvm::StringRef> value, bool negated_name)
-> bool {
-> ErrorOr<Success> {
// If this argument has a meta action, replace the current meta action with
// it.
if (arg.meta_action) {
@@ -932,10 +934,10 @@ auto Parser::ParseArg(const Arg& arg, bool short_spelling,
std::string name;
if (short_spelling) {
name = llvm::formatv("'-{0}' (short for '--{1}')", arg.info.short_name,
name = llvm::formatv("`-{0}` (short for `--{1}`)", arg.info.short_name,
arg.info.name);
} else {
name = llvm::formatv("'--{0}'", arg.info.name);
name = llvm::formatv("`--{0}`", arg.info.name);
}
if (!value) {
@@ -943,15 +945,14 @@ auto Parser::ParseArg(const Arg& arg, bool short_spelling,
// an option and handle it as such.
if (arg.kind == Arg::Kind::MetaActionOnly) {
// Nothing further to do here, this is only a meta-action.
return true;
return Success();
}
if (!arg.has_default) {
*errors_ << "ERROR: Option " << name
<< " requires a value to be provided and none was.\n";
return false;
return Error(llvm::formatv(
"option {0} requires a value to be provided and none was", name));
}
SetOptionDefault(arg);
return true;
return Success();
}
// There is a value to parse as part of the argument.
@@ -963,11 +964,10 @@ auto Parser::ParseArg(const Arg& arg, bool short_spelling,
case Arg::Kind::OneOf:
return ParseOneOfArgValue(arg, *value);
case Arg::Kind::MetaActionOnly:
*errors_ << "ERROR: Option " << name
<< " cannot be used with a value, and '" << *value
<< "' was provided.\n";
// TODO: improve message
return false;
// TODO: Improve message.
return Error(llvm::formatv(
"option {0} cannot be used with a value, and '{1}' was provided",
name, value));
case Arg::Kind::Flag:
case Arg::Kind::Invalid:
CARBON_FATAL("Invalid kind!");
@@ -986,7 +986,7 @@ auto Parser::SplitValue(llvm::StringRef& unparsed_arg)
return value;
}
auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> bool {
auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> ErrorOr<Success> {
CARBON_CHECK(unparsed_arg.starts_with("--") && unparsed_arg.size() > 2,
"Must only be called on a potential long option.");
@@ -997,10 +997,9 @@ auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> bool {
auto option_it = option_map_.find(unparsed_arg);
if (option_it == option_map_.end()) {
*errors_ << "ERROR: Unknown option '--" << (negated_name ? "no-" : "")
<< unparsed_arg << "'\n";
// TODO: improve error
return false;
// TODO: Improve error.
return Error(llvm::formatv("unknown option `--{0}{1}`",
negated_name ? "no-" : "", unparsed_arg));
}
// Mark this option as parsed.
@@ -1011,41 +1010,38 @@ auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> bool {
return ParseArg(option, /*short_spelling=*/false, value, negated_name);
}
auto Parser::ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> bool {
auto Parser::ParseShortOptionSeq(llvm::StringRef unparsed_arg)
-> ErrorOr<Success> {
CARBON_CHECK(unparsed_arg.starts_with("-") && unparsed_arg.size() > 1,
"Must only be called on a potential short option sequence.");
unparsed_arg = unparsed_arg.drop_front();
std::optional<llvm::StringRef> value = SplitValue(unparsed_arg);
if (value && unparsed_arg.size() != 1) {
*errors_ << "ERROR: Cannot provide a value to the group of multiple short "
"options '-"
<< unparsed_arg
<< "=...'; values must be provided to a single option, using "
"either the short or long spelling.\n";
return false;
return Error(llvm::formatv(
"cannot provide a value to the group of multiple short options "
"`-{0}=...`; values must be provided to a single option, using "
"either the short or long spelling",
unparsed_arg));
}
for (unsigned char c : unparsed_arg) {
auto* arg_entry =
(c < short_option_table_.size()) ? short_option_table_[c] : nullptr;
if (!arg_entry) {
*errors_ << "ERROR: Unknown short option '" << c << "'\n";
return false;
return Error(llvm::formatv("unknown short option `{0}`", c));
}
// Mark this argument as parsed.
arg_entry->setInt(true);
// Parse the argument, including the value if this is the last.
const Arg& arg = *arg_entry->getPointer();
if (!ParseArg(arg, /*short_spelling=*/true, value)) {
return false;
}
CARBON_RETURN_IF_ERROR(ParseArg(arg, /*short_spelling=*/true, value));
}
return true;
return Success();
}
auto Parser::FinalizeParsedOptions() -> bool {
auto Parser::FinalizeParsedOptions() -> ErrorOr<Success> {
llvm::SmallVector<const Arg*> missing_options;
for (const auto& option_entry : option_map_) {
const Arg* option = option_entry.second.getPointer();
@@ -1062,7 +1058,7 @@ auto Parser::FinalizeParsedOptions() -> bool {
}
}
if (missing_options.empty()) {
return true;
return Success();
}
// Sort the missing arguments by name to provide a stable and deterministic
@@ -1073,23 +1069,24 @@ auto Parser::FinalizeParsedOptions() -> bool {
return lhs->info.name < rhs->info.name;
});
std::string error_str = "required options not provided: ";
llvm::raw_string_ostream error(error_str);
llvm::ListSeparator sep;
for (const Arg* option : missing_options) {
*errors_ << "ERROR: Required option '--" << option->info.name
<< "' not provided.\n";
error << sep << "--" << option->info.name;
}
return false;
return Error(error_str);
}
auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg) -> bool {
auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg)
-> ErrorOr<Success> {
if (static_cast<size_t>(positional_arg_index_) >=
command_->positional_args.size()) {
*errors_ << "ERROR: Completed parsing all "
<< command_->positional_args.size()
<< " configured positional arguments, and found an additional "
"positional argument: '"
<< unparsed_arg << "'\n";
return false;
return Error(llvm::formatv(
"completed parsing all {0} configured positional arguments, and found "
"an additional positional argument: `{1}`",
command_->positional_args.size(), unparsed_arg));
}
const Arg& arg = *command_->positional_args[positional_arg_index_];
@@ -1107,29 +1104,28 @@ auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg) -> bool {
return ParseArg(arg, /*short_spelling=*/false, unparsed_arg);
}
auto Parser::ParseSubcommand(llvm::StringRef unparsed_arg) -> bool {
auto Parser::ParseSubcommand(llvm::StringRef unparsed_arg) -> ErrorOr<Success> {
auto subcommand_it = subcommand_map_.find(unparsed_arg);
if (subcommand_it == subcommand_map_.end()) {
*errors_ << "ERROR: Invalid subcommand '" << unparsed_arg
<< "'. Available subcommands: ";
error_meta_printer_.PrintSubcommands(*command_);
*errors_ << "\n";
return false;
std::string error_str;
llvm::raw_string_ostream error(error_str);
error << "invalid subcommand `" << unparsed_arg
<< "`; available subcommands: ";
MetaPrinter(&error).PrintSubcommands(*command_);
return Error(error_str);
}
// Before we recurse into the subcommand, verify that all the required
// arguments for this command were in fact parsed.
if (!FinalizeParsedOptions()) {
return false;
}
CARBON_RETURN_IF_ERROR(FinalizeParsedOptions());
// Recurse into the subcommand, tracking the active command.
command_ = subcommand_it->second;
PopulateMaps(*command_);
return true;
return Success();
}
auto Parser::FinalizeParse() -> ParseResult {
auto Parser::FinalizeParse() -> ErrorOr<ParseResult> {
// If an argument action is provided, we run that and consider the parse
// meta-successful rather than verifying required arguments were provided and
// the (sub)command action.
@@ -1139,9 +1135,7 @@ auto Parser::FinalizeParse() -> ParseResult {
}
// Verify we're not missing any arguments.
if (!FinalizeParsedOptions()) {
return ParseResult::Error;
}
CARBON_RETURN_IF_ERROR(FinalizeParsedOptions());
// If we were appending to a positional argument, mark that as complete.
llvm::ArrayRef positional_args = command_->positional_args;
@@ -1159,10 +1153,10 @@ auto Parser::FinalizeParse() -> ParseResult {
// There are un-parsed positional arguments, make sure they aren't required.
const Arg& missing_arg = *unparsed_positional_args.front();
if (missing_arg.is_required) {
*errors_ << "ERROR: Not all required positional arguments were provided. "
"First missing and required positional argument: '"
<< missing_arg.info.name << "'\n";
return ParseResult::Error;
return Error(
llvm::formatv("not all required positional arguments were provided; "
"first missing and required positional argument: `{0}`",
missing_arg.info.name));
}
for (const auto& arg_ptr : unparsed_positional_args) {
CARBON_CHECK(
@@ -1174,11 +1168,13 @@ auto Parser::FinalizeParse() -> ParseResult {
switch (command_->kind) {
case Command::Kind::Invalid:
CARBON_FATAL("Should never have a parser with an invalid command!");
case Command::Kind::RequiresSubcommand:
*errors_ << "ERROR: No subcommand specified. Available subcommands: ";
error_meta_printer_.PrintSubcommands(*command_);
*errors_ << "\n";
return ParseResult::Error;
case Command::Kind::RequiresSubcommand: {
std::string error_str;
llvm::raw_string_ostream error(error_str);
error << "no subcommand specified; available subcommands: ";
MetaPrinter(&error).PrintSubcommands(*command_);
return Error(error_str);
}
case Command::Kind::Action:
// All arguments have been successfully parsed, run any action for the
// most specific selected command. Only the leaf command's action is run.
@@ -1191,7 +1187,7 @@ auto Parser::FinalizeParse() -> ParseResult {
}
auto Parser::ParsePositionalSuffix(
llvm::ArrayRef<llvm::StringRef> unparsed_args) -> bool {
llvm::ArrayRef<llvm::StringRef> unparsed_args) -> ErrorOr<Success> {
CARBON_CHECK(
!command_->positional_args.empty(),
"Cannot do positional suffix parsing without positional arguments!");
@@ -1207,9 +1203,7 @@ auto Parser::ParsePositionalSuffix(
unparsed_args = unparsed_args.drop_front();
if (unparsed_arg != "--") {
if (!ParsePositionalArg(unparsed_arg)) {
return false;
}
CARBON_RETURN_IF_ERROR(ParsePositionalArg(unparsed_arg));
empty_positional = false;
continue;
}
@@ -1218,28 +1212,23 @@ auto Parser::ParsePositionalSuffix(
++positional_arg_index_;
if (static_cast<size_t>(positional_arg_index_) >=
command_->positional_args.size()) {
*errors_
<< "ERROR: Completed parsing all "
<< command_->positional_args.size()
<< " configured positional arguments, but found a subsequent `--` "
"and have no further positional arguments to parse beyond it.\n";
return false;
return Error(
llvm::formatv("completed parsing all {0} configured positional "
"arguments, but found a subsequent `--` and have no "
"further positional arguments to parse beyond it",
command_->positional_args.size()));
}
}
appending_to_positional_arg_ = false;
empty_positional = true;
}
return true;
return Success();
}
Parser::Parser(llvm::raw_ostream* out, llvm::raw_ostream* errors,
CommandInfo command_info,
Parser::Parser(llvm::raw_ostream* out, CommandInfo command_info,
llvm::function_ref<void(CommandBuilder&)> build)
: meta_printer_(out),
errors_(errors),
error_meta_printer_(errors),
root_command_(command_info) {
: meta_printer_(out), root_command_(command_info) {
// Run the command building lambda on a builder for the root command.
CommandBuilder builder(&root_command_, &meta_printer_);
build(builder);
@@ -1248,7 +1237,7 @@ Parser::Parser(llvm::raw_ostream* out, llvm::raw_ostream* errors,
}
auto Parser::Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
-> ParseResult {
-> ErrorOr<ParseResult> {
PopulateMaps(*command_);
while (!unparsed_args.empty()) {
@@ -1258,22 +1247,18 @@ auto Parser::Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
// positional suffix parsing without dropping this argument.
if (unparsed_arg == "--") {
if (command_->positional_args.empty()) {
*errors_ << "ERROR: Cannot meaningfully end option and subcommand "
"arguments with a `--` argument when there are no "
"positional arguments to parse.\n";
return ParseResult::Error;
return Error(
"cannot meaningfully end option and subcommand arguments with a "
"`--` argument when there are no positional arguments to parse");
}
if (static_cast<size_t>(positional_arg_index_) >=
command_->positional_args.size()) {
*errors_
<< "ERROR: Switched to purely positional arguments with a `--` "
"argument despite already having parsed all positional "
"arguments for this command.\n";
return ParseResult::Error;
}
if (!ParsePositionalSuffix(unparsed_args)) {
return ParseResult::Error;
return Error(
"switched to purely positional arguments with a `--` argument "
"despite already having parsed all positional arguments for this "
"command");
}
CARBON_RETURN_IF_ERROR(ParsePositionalSuffix(unparsed_args));
// No more unparsed arguments to handle.
break;
}
@@ -1284,16 +1269,12 @@ auto Parser::Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
if (unparsed_arg.starts_with("--")) {
// Note that the exact argument "--" has been handled above already.
if (!ParseLongOption(unparsed_arg)) {
return ParseResult::Error;
}
CARBON_RETURN_IF_ERROR(ParseLongOption(unparsed_arg));
continue;
}
if (unparsed_arg.starts_with("-") && unparsed_arg.size() > 1) {
if (!ParseShortOptionSeq(unparsed_arg)) {
return ParseResult::Error;
}
CARBON_RETURN_IF_ERROR(ParseShortOptionSeq(unparsed_arg));
continue;
}
@@ -1301,20 +1282,16 @@ auto Parser::Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args)
command_->positional_args.empty() || command_->subcommands.empty(),
"Cannot have both positional arguments and subcommands!");
if (command_->positional_args.empty() && command_->subcommands.empty()) {
*errors_ << "ERROR: Found unexpected positional argument or subcommand: '"
<< unparsed_arg << "'\n";
return ParseResult::Error;
return Error(llvm::formatv(
"found unexpected positional argument or subcommand: `{0}`",
unparsed_arg));
}
if (!command_->positional_args.empty()) {
if (!ParsePositionalArg(unparsed_arg)) {
return ParseResult::Error;
}
CARBON_RETURN_IF_ERROR(ParsePositionalArg(unparsed_arg));
continue;
}
if (!ParseSubcommand(unparsed_arg)) {
return ParseResult::Error;
}
CARBON_RETURN_IF_ERROR(ParseSubcommand(unparsed_arg));
}
return FinalizeParse();
@@ -1531,12 +1508,12 @@ void CommandBuilder::Finalize() {
}
auto Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args,
llvm::raw_ostream& out, llvm::raw_ostream& errors,
CommandInfo command_info,
llvm::function_ref<void(CommandBuilder&)> build) -> ParseResult {
llvm::raw_ostream& out, CommandInfo command_info,
llvm::function_ref<void(CommandBuilder&)> build)
-> ErrorOr<ParseResult> {
// Build a parser, which includes building the command description provided by
// the user.
Parser parser(&out, &errors, command_info, build);
Parser parser(&out, command_info, build);
// Now parse the arguments provided using that parser.
return parser.Parse(unparsed_args);