summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Gennadiy Rozental <rogeeff@google.com>2023-03-08 10:52:30 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2023-03-08 10:53:23 -0800
commit297efcc96b259a706f5cd7dd51e399f1d5f23f9d (patch)
tree1c32913eafb788d74e9765e70eda6654e45d2480
parentb75a3ff4afda9654649328e25555181759d7ad69 (diff)
Introduce two new public APIs for parsing command line flags.
ParseAbseilFlagsOnly To be used to parse Abseil Flags only and classify the rest of the arguments. ReportUnrecognizedFlags To be used to report unrecognized flags. PiperOrigin-RevId: 515082682 Change-Id: I73271b56cc512a5e78b5fcd035564b3672d62ca8
-rw-r--r--absl/flags/parse.cc233
-rw-r--r--absl/flags/parse.h106
-rw-r--r--absl/flags/parse_test.cc159
3 files changed, 342 insertions, 156 deletions
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 768652d8..fcd6a52d 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
+#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
@@ -278,7 +279,7 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
return std::make_tuple("", "", false);
}
- auto equal_sign_pos = arg.find("=");
+ auto equal_sign_pos = arg.find('=');
absl::string_view flag_name = arg.substr(0, equal_sign_pos);
@@ -664,40 +665,76 @@ std::vector<std::string> GetMisspellingHints(const absl::string_view flag) {
// --------------------------------------------------------------------
std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
- UsageFlagsAction usage_flag_act,
- OnUndefinedFlag on_undef_flag) {
- ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
+ UsageFlagsAction usage_flag_action,
+ OnUndefinedFlag undef_flag_action) {
+ std::vector<char*> positional_args;
+ std::vector<UnrecognizedFlag> unrecognized_flags;
- // Once parsing has started we will not have more flag registrations.
- // If we did, they would be missing during parsing, which is a problem on
- // itself.
- flags_internal::FinalizeRegistry();
+ bool parse_successful = absl::ParseAbseilFlagsOnly(
+ argc, argv, positional_args, unrecognized_flags);
- // This routine does not return anything since we abort on failure.
- CheckDefaultValuesParsingRoundtrip();
+ if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
+ ReportUnrecognizedFlags(
+ unrecognized_flags,
+ undef_flag_action ==
+ flags_internal::OnUndefinedFlag::kAbortIfUndefined);
- std::vector<std::string> flagfile_value;
+ if (!unrecognized_flags.empty()) { parse_successful = false; }
+ }
+
+#if ABSL_FLAGS_STRIP_NAMES
+ if (!parse_successful) {
+ ReportUsageError("NOTE: command line flags are disabled in this build",
+ true);
+ }
+#endif
+
+ if (!parse_successful) {
+ HandleUsageFlags(std::cout, ProgramUsageMessage());
+ std::exit(1);
+ }
+
+ if (usage_flag_action == UsageFlagsAction::kHandleUsage) {
+ int exit_code = HandleUsageFlags(std::cout, ProgramUsageMessage());
+
+ if (exit_code != -1) {
+ std::exit(exit_code);
+ }
+ }
+
+ return positional_args;
+}
+// --------------------------------------------------------------------
+
+} // namespace flags_internal
+
+bool ParseAbseilFlagsOnly(int argc, char* argv[],
+ std::vector<char*>& positional_args,
+ std::vector<UnrecognizedFlag>& unrecognized_flags) {
+ ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
+
+ using flags_internal::ArgsList;
+ using flags_internal::specified_flags;
+
+ std::vector<std::string> flagfile_value;
std::vector<ArgsList> input_args;
- input_args.emplace_back(argc, argv);
- std::vector<char*> output_args;
- std::vector<char*> positional_args;
- output_args.reserve(static_cast<size_t>(argc));
+ // Once parsing has started we will not allow more flag registrations.
+ flags_internal::FinalizeRegistry();
- // This is the list of undefined flags. The element of the list is the pair
- // consisting of boolean indicating if flag came from command line (vs from
- // some flag file we've read) and flag name.
- // TODO(rogeeff): Eliminate the first element in the pair after cleanup.
- std::vector<std::pair<bool, std::string>> undefined_flag_names;
+ // This routine does not return anything since we abort on failure.
+ flags_internal::CheckDefaultValuesParsingRoundtrip();
+
+ input_args.push_back(ArgsList(argc, argv));
// Set program invocation name if it is not set before.
- if (ProgramInvocationName() == "UNKNOWN") {
+ if (flags_internal::ProgramInvocationName() == "UNKNOWN") {
flags_internal::SetProgramInvocationName(argv[0]);
}
- output_args.push_back(argv[0]);
+ positional_args.push_back(argv[0]);
- absl::MutexLock l(&specified_flags_guard);
+ absl::MutexLock l(&flags_internal::specified_flags_guard);
if (specified_flags == nullptr) {
specified_flags = new std::vector<const CommandLineFlag*>;
} else {
@@ -709,13 +746,15 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
// recursive parsing of flagfile(s).
bool success = true;
while (!input_args.empty()) {
- // 10. First we process the built-in generator flags.
- success &= HandleGeneratorFlags(input_args, flagfile_value);
+ // First we process the built-in generator flags.
+ success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value);
- // 30. Select top-most (most recent) arguments list. If it is empty drop it
+ // Select top-most (most recent) arguments list. If it is empty drop it
// and re-try.
ArgsList& curr_list = input_args.back();
+ // Every ArgsList starts with real or fake program name, so we can always
+ // start by skipping it.
curr_list.PopFront();
if (curr_list.Size() == 0) {
@@ -723,13 +762,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
continue;
}
- // 40. Pick up the front remaining argument in the current list. If current
- // stack of argument lists contains only one element - we are processing an
- // argument from the original argv.
+ // Handle the next argument in the current list. If the stack of argument
+ // lists contains only one element - we are processing an argument from the
+ // original argv.
absl::string_view arg(curr_list.Front());
bool arg_from_argv = input_args.size() == 1;
- // 50. If argument does not start with - or is just "-" - this is
+ // If argument does not start with '-' or is just "-" - this is
// positional argument.
if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
ABSL_INTERNAL_CHECK(arg_from_argv,
@@ -739,8 +778,8 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
continue;
}
- // 60. Split the current argument on '=' to figure out the argument
- // name and value. If flag name is empty it means we've got "--". value
+ // Split the current argument on '=' to deduce the argument flag name and
+ // value. If flag name is empty it means we've got an "--" argument. Value
// can be empty either if there were no '=' in argument string at all or
// an argument looked like "--foo=". In a latter case is_empty_value is
// true.
@@ -748,10 +787,11 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
absl::string_view value;
bool is_empty_value = false;
- std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg);
+ std::tie(flag_name, value, is_empty_value) =
+ flags_internal::SplitNameAndValue(arg);
- // 70. "--" alone means what it does for GNU: stop flags parsing. We do
- // not support positional arguments in flagfiles, so we just drop them.
+ // Standalone "--" argument indicates that the rest of the arguments are
+ // positional. We do not support positional arguments in flagfiles.
if (flag_name.empty()) {
ABSL_INTERNAL_CHECK(arg_from_argv,
"Flagfile cannot contain positional argument");
@@ -760,36 +800,36 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
break;
}
- // 80. Locate the flag based on flag name. Handle both --foo and --nofoo
+ // Locate the flag based on flag name. Handle both --foo and --nofoo.
CommandLineFlag* flag = nullptr;
bool is_negative = false;
- std::tie(flag, is_negative) = LocateFlag(flag_name);
+ std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name);
if (flag == nullptr) {
// Usage flags are not modeled as Abseil flags. Locate them separately.
if (flags_internal::DeduceUsageFlags(flag_name, value)) {
continue;
}
-
- if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
- undefined_flag_names.emplace_back(arg_from_argv,
- std::string(flag_name));
- }
+ unrecognized_flags.emplace_back(arg_from_argv
+ ? UnrecognizedFlag::kFromArgv
+ : UnrecognizedFlag::kFromFlagfile,
+ flag_name);
continue;
}
- // 90. Deduce flag's value (from this or next argument)
+ // Deduce flag's value (from this or next argument).
bool value_success = true;
- std::tie(value_success, value) =
- DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list);
+ std::tie(value_success, value) = flags_internal::DeduceFlagValue(
+ *flag, value, is_negative, is_empty_value, &curr_list);
success &= value_success;
- // 100. Set the located flag to a new new value, unless it is retired.
- // Setting retired flag fails, but we ignoring it here while also reporting
- // access to retired flag.
+ // Set the located flag to a new value, unless it is retired. Setting
+ // retired flag fails, but we ignoring it here while also reporting access
+ // to retired flag.
std::string error;
if (!flags_internal::PrivateHandleAccessor::ParseFrom(
- *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
+ *flag, value, flags_internal::SET_FLAGS_VALUE,
+ flags_internal::kCommandLine, error)) {
if (flag->IsRetired()) continue;
flags_internal::ReportUsageError(error, true);
@@ -799,71 +839,62 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
}
}
- for (const auto& flag_name : undefined_flag_names) {
- if (CanIgnoreUndefinedFlag(flag_name.second)) continue;
- // Verify if flag_name has the "no" already removed
- std::vector<std::string> flags;
- if (flag_name.first) flags = GetMisspellingHints(flag_name.second);
- if (flags.empty()) {
- flags_internal::ReportUsageError(
- absl::StrCat("Unknown command line flag '", flag_name.second, "'"),
- true);
- } else {
- flags_internal::ReportUsageError(
- absl::StrCat("Unknown command line flag '", flag_name.second,
- "'. Did you mean: ", absl::StrJoin(flags, ", "), " ?"),
- true);
- }
-
- success = false;
- }
-
-#if ABSL_FLAGS_STRIP_NAMES
- if (!success) {
- flags_internal::ReportUsageError(
- "NOTE: command line flags are disabled in this build", true);
- }
-#endif
-
- if (!success) {
- flags_internal::HandleUsageFlags(std::cout,
- ProgramUsageMessage());
- std::exit(1);
- }
-
- if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
- int exit_code = flags_internal::HandleUsageFlags(
- std::cout, ProgramUsageMessage());
-
- if (exit_code != -1) {
- std::exit(exit_code);
- }
- }
-
- ResetGeneratorFlags(flagfile_value);
-
- // Reinstate positional args which were intermixed with flags in the arguments
- // list.
- for (auto arg : positional_args) {
- output_args.push_back(arg);
- }
+ flags_internal::ResetGeneratorFlags(flagfile_value);
// All the remaining arguments are positional.
if (!input_args.empty()) {
for (size_t arg_index = input_args.back().FrontIndex();
arg_index < static_cast<size_t>(argc); ++arg_index) {
- output_args.push_back(argv[arg_index]);
+ positional_args.push_back(argv[arg_index]);
}
}
// Trim and sort the vector.
specified_flags->shrink_to_fit();
std::sort(specified_flags->begin(), specified_flags->end(),
- SpecifiedFlagsCompare{});
- return output_args;
+ flags_internal::SpecifiedFlagsCompare{});
+
+ // Filter out unrecognized flags, which are ok to ignore.
+ std::vector<UnrecognizedFlag> filtered;
+ filtered.reserve(unrecognized_flags.size());
+ for (const auto& unrecognized : unrecognized_flags) {
+ if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name))
+ continue;
+ filtered.push_back(unrecognized);
+ }
+
+ std::swap(unrecognized_flags, filtered);
+
+ return success;
}
-} // namespace flags_internal
+// --------------------------------------------------------------------
+
+void ReportUnrecognizedFlags(
+ const std::vector<UnrecognizedFlag>& unrecognized_flags,
+ bool report_fatal_error) {
+ for (const auto& unrecognized : unrecognized_flags) {
+ // Verify if flag_name has the "no" already removed
+ std::vector<std::string> misspelling_hints;
+ if (unrecognized.source == UnrecognizedFlag::kFromArgv) {
+ misspelling_hints =
+ flags_internal::GetMisspellingHints(unrecognized.flag_name);
+ }
+
+ if (misspelling_hints.empty()) {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+ "'"),
+ report_fatal_error);
+ } else {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+ "'. Did you mean: ",
+ absl::StrJoin(misspelling_hints, ", "), " ?"),
+ report_fatal_error);
+ }
+ }
+}
// --------------------------------------------------------------------
diff --git a/absl/flags/parse.h b/absl/flags/parse.h
index 929de2cb..a5629375 100644
--- a/absl/flags/parse.h
+++ b/absl/flags/parse.h
@@ -23,35 +23,103 @@
#ifndef ABSL_FLAGS_PARSE_H_
#define ABSL_FLAGS_PARSE_H_
+#include <string>
#include <vector>
#include "absl/base/config.h"
#include "absl/flags/internal/parse.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
+// This type represent information about an unrecognized flag in the command
+// line.
+struct UnrecognizedFlag {
+ enum Source { kFromArgv, kFromFlagfile };
+
+ explicit UnrecognizedFlag(Source s, absl::string_view f)
+ : source(s), flag_name(f) {}
+ // This field indicates where we found this flag: on the original command line
+ // or read in some flag file.
+ Source source;
+ // Name of the flag we did not recognize in --flag_name=value or --flag_name.
+ std::string flag_name;
+};
+
+inline bool operator==(const UnrecognizedFlag& lhs,
+ const UnrecognizedFlag& rhs) {
+ return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name;
+}
+
+// ParseAbseilFlagsOnly()
+//
+// Parses a list of command-line arguments, passed in the `argc` and `argv[]`
+// parameters, into a set of Abseil Flag values, returning any unparsed
+// arguments in `positional_args` and `unrecognized_flags` output parameters.
+//
+// This function classifies all the arguments (including content of the
+// flagfiles, if any) into one of the following groups:
+//
+// * arguments specified as "--flag=value" or "--flag value" that match
+// registered or built-in Abseil Flags. These are "Abseil Flag arguments."
+// * arguments specified as "--flag" that are unrecognized as Abseil Flags
+// * arguments that are not specified as "--flag" are positional arguments
+// * arguments that follow the flag-terminating delimiter (`--`) are also
+// treated as positional arguments regardless of their syntax.
+//
+// All of the deduced Abseil Flag arguments are then parsed into their
+// corresponding flag values.
+//
+// All the remaining positional arguments including original program name
+// (argv[0]) are are returned in the `positional_args` output parameter.
+//
+// All unrecognized flags that are not otherwise ignored are returned in the
+// `unrecognized_flags` output parameter. Note that the special `undefok`
+// flag allows you to specify flags which can be safely ignored; `undefok`
+// specifies these flags as a comma-separated list. Any unrecognized flags
+// that appear within `undefok` will therefore be ignored and not included in
+// the `unrecognized_flag` output parameter.
+//
+// This function returns true if no syntax errors were found on the command line
+// or in the referenced flag files. Unrecognized flags do not cause this routine
+// to return false.
+bool ParseAbseilFlagsOnly(int argc, char* argv[],
+ std::vector<char*>& positional_args,
+ std::vector<UnrecognizedFlag>& unrecognized_flags);
+
+// ReportUnrecognizedFlags()
+//
+// Reports an error for all non-ignored unrecognized flags in the provided
+// `unrecognized_flags` list.
+//
+// If `report_fatal_error` is true, the fatal error is reported and program is
+// aborted. Otherwise non-fatal error is reported for all flags.
+//
+// This function returns true if any non-ignored unrecognized flags were
+// located in the list and false otherwise.
+void ReportUnrecognizedFlags(
+ const std::vector<UnrecognizedFlag>& unrecognized_flags,
+ bool report_fatal_error);
+
// ParseCommandLine()
//
-// Parses the set of command-line arguments passed in the `argc` (argument
-// count) and `argv[]` (argument vector) parameters from `main()`, assigning
-// values to any defined Abseil flags. (Any arguments passed after the
-// flag-terminating delimiter (`--`) are treated as positional arguments and
-// ignored.)
-//
-// Any command-line flags (and arguments to those flags) are parsed into Abseil
-// Flag values, if those flags are defined. Any undefined flags will either
-// return an error, or be ignored if that flag is designated using `undefok` to
-// indicate "undefined is OK."
-//
-// Any command-line positional arguments not part of any command-line flag (or
-// arguments to a flag) are returned in a vector, with the program invocation
-// name at position 0 of that vector. (Note that this includes positional
-// arguments after the flag-terminating delimiter `--`.)
-//
-// After all flags and flag arguments are parsed, this function looks for any
-// built-in usage flags (e.g. `--help`), and if any were specified, it reports
-// help messages and then exits the program.
+// First parses Abseil Flags only from the command line according to the
+// description in `ParseAbseilFlagsOnly`. In addition this function handles
+// unrecognized and usage flags.
+//
+// If any unrecognized flags are located they are reported using
+// `ReportUnrecognizedFlags`.
+//
+// If any errors detected during command line parsing, this routine reports a
+// usage message and aborts the program.
+//
+// If any built-in usage flags were specified on the command line (e.g.
+// `--help`), this function reports help messages and then gracefully exits the
+// program.
+//
+// This function returns all the remaining positional arguments collected by
+// `ParseAbseilFlagsOnly`.
std::vector<char*> ParseCommandLine(int argc, char* argv[]);
ABSL_NAMESPACE_END
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 18a01377..c46bb46e 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -20,13 +20,13 @@
#include <cstddef>
#include <fstream>
#include <string>
+#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h"
-#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/usage.h"
@@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = {
// Builds flagfile flag in the flagfile_flag buffer and returns it. This
// function also creates a temporary flagfile based on FlagfileData input.
// We create a flagfile in a temporary directory with the name specified in
-// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
+// FlagfileData and populate it with lines specified in FlagfileData. If $0 is
// referenced in any of the lines in FlagfileData they are replaced with
// temporary directory location. This way we can test inclusion of one flagfile
// from another flagfile.
@@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
// --------------------------------------------------------------------
template <int N>
+bool InvokeParseAbslOnly(const char* (&in_argv)[N]) {
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ return absl::ParseAbseilFlagsOnly(N, const_cast<char**>(in_argv),
+ positional_args, unrecognized_flags);
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
void TestParse(const char* (&in_argv)[N], int int_flag_value,
double double_flag_val, absl::string_view string_flag_val,
bool bool_flag_val, int exp_position_args = 0) {
@@ -854,26 +865,6 @@ TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
// --------------------------------------------------------------------
-TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
- const char* in_args1[] = {
- "testbin",
- "arg1",
- "--undef_flag=aa",
- "--int_flag=21",
- };
-
- auto out_args1 = flags::ParseCommandLineImpl(
- 4, const_cast<char**>(in_args1), flags::UsageFlagsAction::kHandleUsage,
- flags::OnUndefinedFlag::kIgnoreUndefined);
-
- EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
- absl::string_view("arg1")}));
-
- EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
-}
-
-// --------------------------------------------------------------------
-
TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
const char* in_args1[] = {
"testbin",
@@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
"--int_flag=3",
};
- auto out_args2 = flags::ParseCommandLineImpl(
- 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
+ InvokeParseAbslOnly(in_args2);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
+
+ const char* in_args3[] = {"testbin", "--help", "some_positional_arg"};
+
+ InvokeParseAbslOnly(in_args3);
+
+ EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
}
// --------------------------------------------------------------------
@@ -904,20 +899,10 @@ TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
"--help=abcd",
};
- auto out_args1 = flags::ParseCommandLineImpl(
- 2, const_cast<char**>(in_args1), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
+ InvokeParseAbslOnly(in_args1);
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
-
- const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
-
- auto out_args2 = flags::ParseCommandLineImpl(
- 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage,
- flags::OnUndefinedFlag::kAbortIfUndefined);
-
- EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
}
// --------------------------------------------------------------------
@@ -942,4 +927,106 @@ TEST_F(ParseTest, WasPresentOnCommandLine) {
// --------------------------------------------------------------------
+TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
+ const char* in_args[] = {
+ "testbin",
+ "arg1",
+ "--bool_flag",
+ "--int_flag=211",
+ "arg2",
+ "--double_flag=1.1",
+ "--undef_flag1",
+ "--undef_flag2=123",
+ "--string_flag",
+ "asd",
+ "--",
+ "--some_flag",
+ "arg4",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args,
+ ElementsAreArray(
+ {absl::string_view("testbin"), absl::string_view("arg1"),
+ absl::string_view("arg2"), absl::string_view("--some_flag"),
+ absl::string_view("arg4")}));
+ EXPECT_THAT(unrecognized_flags,
+ ElementsAreArray(
+ {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag1"),
+ absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag2")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, ParseAbseilFlagsOnlyFailure) {
+ const char* in_args[] = {
+ "testbin",
+ "--int_flag=21.1",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_FALSE(absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, UndefOkFlagsAreIgnored) {
+ const char* in_args[] = {
+ "testbin", "--undef_flag1",
+ "--undef_flag2=123", "--undefok=undef_flag2",
+ "--undef_flag3", "value",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"),
+ absl::string_view("value")}));
+ EXPECT_THAT(unrecognized_flags,
+ ElementsAreArray(
+ {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag1"),
+ absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+ "undef_flag3")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) {
+ const char* in_args[] = {
+ "testbin",
+ "--undef_flag1",
+ "--undef_flag2=123",
+ "--undefok=undef_flag2,undef_flag1,undef_flag3",
+ "--undef_flag3",
+ "value",
+ "--",
+ "--undef_flag4",
+ };
+
+ std::vector<char*> positional_args;
+ std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+ EXPECT_TRUE(absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args),
+ positional_args, unrecognized_flags));
+ EXPECT_THAT(positional_args,
+ ElementsAreArray({absl::string_view("testbin"),
+ absl::string_view("value"),
+ absl::string_view("--undef_flag4")}));
+ EXPECT_THAT(unrecognized_flags, testing::IsEmpty());
+}
+
+// --------------------------------------------------------------------
+
} // namespace