mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
+157
-180
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user