art: Refactor RuntimeOptions/ParsedOptions

Refactor the RuntimeOptions to be a
type-safe map (VariantMap, see runtime_options.h) and the ParsedOptions
to delegate the parsing to CmdlineParser (see cmdline/cmdline_parser.h).

This is the start of a command line parsing refactor, and may include
more in the future (dex2oat, patchoat, etc).

For more details of the command line parsing generator usage see cmdline/README.md

Change-Id: Ic67c6bca5e1f33bf2ec60e2e3ff8c366bab91563
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
new file mode 100644
index 0000000..a555356
--- /dev/null
+++ b/cmdline/cmdline_parser.h
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_PARSER_H_
+
+#define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
+
+#include "cmdline/detail/cmdline_parser_detail.h"
+#include "cmdline/detail/cmdline_parse_argument_detail.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+
+#include "cmdline_type_parser.h"
+#include "token_range.h"
+#include "cmdline_types.h"
+#include "cmdline_result.h"
+#include "cmdline_parse_result.h"
+
+#include "runtime/base/variant_map.h"
+#include "utils.h"
+
+#include <vector>
+#include <memory>
+
+namespace art {
+// Build a parser for command line arguments with a small domain specific language.
+// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
+// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser {
+  template <typename TArg>
+  struct ArgumentBuilder;
+
+  struct Builder;  // Build the parser.
+  struct UntypedArgumentBuilder;  // Build arguments which weren't yet given a type.
+
+ private:
+  // Forward declare some functions that we need to use before fully-defining structs.
+  template <typename TArg>
+  static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
+  static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
+
+  // Allow argument definitions to save their values when they are parsed,
+  // without having a dependency on CmdlineParser or any of the builders.
+  //
+  // A shared pointer to the save destination is saved into the load/save argument callbacks.
+  //
+  // This also allows the underlying storage (i.e. a variant map) to be released
+  // to the user, without having to recreate all of the callbacks.
+  struct SaveDestination {
+    SaveDestination() : variant_map_(new TVariantMap()) {}
+
+    // Save value to the variant map.
+    template <typename TArg>
+    void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
+      variant_map_->Set(key, value);
+    }
+
+    // Get the existing value from a map, creating the value if it did not already exist.
+    template <typename TArg>
+    TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
+      auto* ptr = variant_map_->Get(key);
+      if (ptr == nullptr) {
+        variant_map_->Set(key, TArg());
+        ptr = variant_map_->Get(key);
+        assert(ptr != nullptr);
+      }
+
+      return *ptr;
+    }
+
+   protected:
+    // Release the map, clearing it as a side-effect.
+    // Future saves will be distinct from previous saves.
+    TVariantMap&& ReleaseMap() {
+      return std::move(*variant_map_);
+    }
+
+    // Get a read-only reference to the variant map.
+    const TVariantMap& GetMap() {
+      return *variant_map_;
+    }
+
+    // Clear all potential save targets.
+    void Clear() {
+      variant_map_->Clear();
+    }
+
+   private:
+    // Don't try to copy or move this. Just don't.
+    SaveDestination(const SaveDestination&) = delete;
+    SaveDestination(SaveDestination&&) = delete;
+    SaveDestination& operator=(const SaveDestination&) = delete;
+    SaveDestination& operator=(SaveDestination&&) = delete;
+
+    std::shared_ptr<TVariantMap> variant_map_;
+
+    // Allow the parser to change the underlying pointers when we release the underlying storage.
+    friend struct CmdlineParser;
+  };
+
+ public:
+  // Builder for the argument definition of type TArg. Do not use this type directly,
+  // it is only a separate type to provide compile-time enforcement against doing
+  // illegal builds.
+  template <typename TArg>
+  struct ArgumentBuilder {
+    // Add a range check to this argument.
+    ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
+      argument_info_.has_range_ = true;
+      argument_info_.min_ = min;
+      argument_info_.max_ = max;
+
+      return *this;
+    }
+
+    // Map the list of names into the list of values. List of names must not have
+    // any wildcards '_' in it.
+    //
+    // Do not use if a value map has already been set.
+    ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
+      SetValuesInternal(value_list);
+      return *this;
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Map the parsed string values (from _) onto a concrete value. If no wildcard
+    // has been specified, then map the value directly from the arg name (i.e.
+    // if there are multiple aliases, then use the alias to do the mapping).
+    //
+    // Do not use if a values list has already been set.
+    ArgumentBuilder<TArg>& WithValueMap(
+        std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
+      assert(!argument_info_.has_value_list_);
+
+      argument_info_.has_value_map_ = true;
+      argument_info_.value_map_ = key_value_list;
+
+      return *this;
+    }
+
+    // If this argument is seen multiple times, successive arguments mutate the same value
+    // instead of replacing it with a new value.
+    ArgumentBuilder<TArg>& AppendValues() {
+      argument_info_.appending_values_ = true;
+
+      return *this;
+    }
+
+    // Convenience type alias for the variant map key type definition.
+    using MapKey = TVariantMapKey<TArg>;
+
+    // Write the results of this argument into the key.
+    // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
+    CmdlineParser::Builder& IntoKey(const MapKey& key) {
+      // Only capture save destination as a pointer.
+      // This allows the parser to later on change the specific save targets.
+      auto save_destination = save_destination_;
+      save_value_ = [save_destination, &key](TArg& value) {
+        save_destination->SaveToMap(key, value);
+        CMDLINE_DEBUG_LOG << "Saved value into map '"
+            << detail::ToStringAny(value) << "'" << std::endl;
+      };
+
+      load_value_ = [save_destination, &key]() -> TArg& {
+        TArg& value = save_destination->GetOrCreateFromMap(key);
+        CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
+            << std::endl;
+
+        return value;
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    // Ensure we always move this when returning a new builder.
+    ArgumentBuilder(ArgumentBuilder&&) = default;
+
+   protected:
+    // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
+    CmdlineParser::Builder& IntoIgnore() {
+      save_value_ = [](TArg& value) {
+        CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
+      };
+      load_value_ = []() -> TArg& {
+        assert(false && "Should not be appending values to ignored arguments");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    void SetValuesInternal(const std::vector<TArg>&& value_list) {
+      assert(!argument_info_.has_value_map_);
+
+      argument_info_.has_value_list_ = true;
+      argument_info_.value_list_ = value_list;
+    }
+
+    void SetNames(std::vector<const char*>&& names) {
+      argument_info_.names_ = names;
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      argument_info_.names_ = names;
+    }
+
+   private:
+    // Copying is bad. Move only.
+    ArgumentBuilder(const ArgumentBuilder&) = delete;
+
+    // Called by any function that doesn't chain back into this builder.
+    // Completes the argument builder and save the information into the main builder.
+    void CompleteArgument() {
+      assert(save_value_specified_ &&
+             "No Into... function called, nowhere to save parsed values to");
+      assert(load_value_specified_ &&
+             "No Into... function called, nowhere to load parsed values from");
+
+      argument_info_.CompleteArgument();
+
+      // Appending the completed argument is destructive. The object is no longer
+      // usable since all the useful information got moved out of it.
+      AppendCompletedArgument(parent_,
+                              new detail::CmdlineParseArgument<TArg>(
+                                  std::move(argument_info_),
+                                  std::move(save_value_),
+                                  std::move(load_value_)));
+    }
+
+    friend struct CmdlineParser;
+    friend struct CmdlineParser::Builder;
+    friend struct CmdlineParser::UntypedArgumentBuilder;
+
+    ArgumentBuilder(CmdlineParser::Builder& parser,
+                    std::shared_ptr<SaveDestination> save_destination)
+        : parent_(parser),
+          save_value_specified_(false),
+          load_value_specified_(false),
+          save_destination_(save_destination) {
+      save_value_ = [](TArg&) {
+        assert(false && "No save value function defined");
+      };
+
+      load_value_ = []() -> TArg& {
+        assert(false && "No load value function defined");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+    }
+
+    CmdlineParser::Builder& parent_;
+    std::function<void(TArg&)> save_value_;
+    std::function<TArg&(void)> load_value_;
+    bool save_value_specified_;
+    bool load_value_specified_;
+    detail::CmdlineParserArgumentInfo<TArg> argument_info_;
+
+    std::shared_ptr<SaveDestination> save_destination_;
+  };
+
+  struct UntypedArgumentBuilder {
+    // Set a type for this argument. The specific subcommand parser is looked up by the type.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithType() {
+      return CreateTypedBuilder<TArg>();
+    }
+
+    // When used with multiple aliases, map the position of the alias to the value position.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
+      auto&& a = CreateTypedBuilder<TArg>();
+      a.WithValues(values);
+      return std::move(a);
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Set the current building argument to target this key.
+    // When this command line argument is parsed, it can be fetched with this key.
+    Builder& IntoKey(const TVariantMapKey<Unit>& key) {
+      return CreateTypedBuilder<Unit>().IntoKey(key);
+    }
+
+    // Ensure we always move this when returning a new builder.
+    UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
+
+   protected:
+    void SetNames(std::vector<const char*>&& names) {
+      names_ = std::move(names);
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      names_ = names;
+    }
+
+   private:
+    // No copying. Move instead.
+    UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
+
+    template <typename TArg>
+    ArgumentBuilder<TArg> CreateTypedBuilder() {
+      auto&& b = CreateArgumentBuilder<TArg>(parent_);
+      InitializeTypedBuilder(&b);  // Type-specific initialization
+      b.SetNames(std::move(names_));
+      return std::move(b);
+    }
+
+    template <typename TArg = Unit>
+    typename std::enable_if<std::is_same<TArg, Unit>::value>::type
+    InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
+      // Every Unit argument implicitly maps to a runtime value of Unit{}
+      std::vector<Unit> values(names_.size(), Unit{});  // NOLINT [whitespace/braces] [5]
+      arg_builder->SetValuesInternal(std::move(values));
+    }
+
+    // No extra work for all other types
+    void InitializeTypedBuilder(void*) {}
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct Builder;
+
+    explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
+    // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
+
+    CmdlineParser::Builder& parent_;
+    std::vector<const char*> names_;
+  };
+
+  // Build a new parser given a chain of calls to define arguments.
+  struct Builder {
+    Builder() : save_destination_(new SaveDestination()) {}
+
+    // Define a single argument. The default type is Unit.
+    UntypedArgumentBuilder Define(const char* name) {
+      return Define({name});
+    }
+
+    // Define a single argument with multiple aliases.
+    UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
+      auto&& b = UntypedArgumentBuilder(*this);
+      b.SetNames(names);
+      return std::move(b);
+    }
+
+    // Whether the parser should give up on unrecognized arguments. Not recommended.
+    Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
+      ignore_unrecognized_ = ignore_unrecognized;
+      return *this;
+    }
+
+    // Provide a list of arguments to ignore for backwards compatibility.
+    Builder& Ignore(std::initializer_list<const char*> ignore_list) {
+      for (auto&& ignore_name : ignore_list) {
+        std::string ign = ignore_name;
+
+        // Ignored arguments are just like a regular definition which have very
+        // liberal parsing requirements (no range checks, no value checks).
+        // Unlike regular argument definitions, when a value gets parsed into its
+        // stronger type, we just throw it away.
+
+        if (ign.find("_") != std::string::npos) {  // Does the arg-def have a wildcard?
+          // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
+          auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        } else {
+          // pretend this is a unit, e.g. -Xjitblocking
+          auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        }
+      }
+      ignore_list_ = ignore_list;
+      return *this;
+    }
+
+    // Finish building the parser; performs sanity checks. Return value is moved, not copied.
+    // Do not call this more than once.
+    CmdlineParser Build() {
+      assert(!built_);
+      built_ = true;
+
+      auto&& p = CmdlineParser(ignore_unrecognized_,
+                               std::move(ignore_list_),
+                               save_destination_,
+                               std::move(completed_arguments_));
+
+      return std::move(p);
+    }
+
+   protected:
+    void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
+      auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
+      completed_arguments_.push_back(std::move(smart_ptr));
+    }
+
+   private:
+    // No copying now!
+    Builder(const Builder& other) = delete;
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct UntypedArgumentBuilder;
+    friend struct CmdlineParser;
+
+    bool built_ = false;
+    bool ignore_unrecognized_ = false;
+    std::vector<const char*> ignore_list_;
+    std::shared_ptr<SaveDestination> save_destination_;
+
+    std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+  };
+
+  CmdlineResult Parse(const std::string& argv) {
+    std::vector<std::string> tokenized;
+    Split(argv, ' ', &tokenized);
+
+    return Parse(TokenRange(std::move(tokenized)));
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  CmdlineResult Parse(const char* argv) {
+    return Parse(std::string(argv));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<const char*>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<std::string>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
+  // Assumes that argv[0] is the program name, and ignores it.
+  CmdlineResult Parse(const char* argv[], int argc) {
+    return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
+  }
+
+  // Look up the arguments that have been parsed; use the target keys to lookup individual args.
+  const TVariantMap& GetArgumentsMap() const {
+    return save_destination_->GetMap();
+  }
+
+  // Release the arguments map that has been parsed; useful for move semantics.
+  TVariantMap&& ReleaseArgumentsMap() {
+    return save_destination_->ReleaseMap();
+  }
+
+  // How many arguments were defined?
+  size_t CountDefinedArguments() const {
+    return completed_arguments_.size();
+  }
+
+  // Ensure we have a default move constructor.
+  CmdlineParser(CmdlineParser&& other) = default;
+  // Ensure we have a default move assignment operator.
+  CmdlineParser& operator=(CmdlineParser&& other) = default;
+
+ private:
+  friend struct Builder;
+
+  // Construct a new parser from the builder. Move all the arguments.
+  explicit CmdlineParser(bool ignore_unrecognized,
+                         std::vector<const char*>&& ignore_list,
+                         std::shared_ptr<SaveDestination> save_destination,
+                         std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&&
+                             completed_arguments)
+    : ignore_unrecognized_(ignore_unrecognized),
+      ignore_list_(std::move(ignore_list)),
+      save_destination_(save_destination),
+      completed_arguments_(std::move(completed_arguments)) {
+    assert(save_destination != nullptr);
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  // The parsing will fail on the first non-success parse result and return that error.
+  //
+  // All previously-parsed arguments are cleared out.
+  // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
+  // A partial parse will result only in a partial save of the arguments.
+  CmdlineResult Parse(TokenRange&& arguments_list) {
+    save_destination_->Clear();
+
+    for (size_t i = 0; i < arguments_list.Size(); ) {
+      TokenRange possible_name = arguments_list.Slice(i);
+
+      size_t best_match_size = 0;  // How many tokens were matched in the best case.
+      size_t best_match_arg_idx = 0;
+      bool matched = false;  // At least one argument definition has been matched?
+
+      // Find the closest argument definition for the remaining token range.
+      size_t arg_idx = 0;
+      for (auto&& arg : completed_arguments_) {
+        size_t local_match = arg->MaybeMatches(possible_name);
+
+        if (local_match > best_match_size) {
+          best_match_size = local_match;
+          best_match_arg_idx = arg_idx;
+          matched = true;
+        }
+        arg_idx++;
+      }
+
+      // Saw some kind of unknown argument
+      if (matched == false) {
+        if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
+          // Consume 1 token and keep going, hopefully the next token is a good one.
+          ++i;
+          continue;
+        }
+        // Common case:
+        // Bail out on the first unknown argument with an error.
+        return CmdlineResult(CmdlineResult::kUnknown,
+                             std::string("Unknown argument: ") + possible_name[0]);
+      }
+
+      // Look at the best-matched argument definition and try to parse against that.
+      auto&& arg = completed_arguments_[best_match_arg_idx];
+
+      assert(arg->MaybeMatches(possible_name) == best_match_size);
+
+      // Try to parse the argument now, if we have enough tokens.
+      std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
+      size_t min_tokens;
+      size_t max_tokens;
+
+      std::tie(min_tokens, max_tokens) = num_tokens;
+
+      if ((i + min_tokens) > arguments_list.Size()) {
+        // expected longer command line but it was too short
+        // e.g. if the argv was only "-Xms" without specifying a memory option
+        CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
+            " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") +
+                             possible_name[0] + ": incomplete command line arguments, expected "
+                             + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
+                             " more tokens");
+      }
+
+      if (best_match_size > max_tokens || best_match_size < min_tokens) {
+        // Even our best match was out of range, so parsing would fail instantly.
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") + possible_name[0] + ": too few tokens "
+                             "matched " + std::to_string(best_match_size)
+                             + " but wanted " + std::to_string(num_tokens.first));
+      }
+
+      // We have enough tokens to begin exact parsing.
+      TokenRange exact_range = possible_name.Slice(0, max_tokens);
+
+      size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
+      CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
+
+      if (parse_attempt.IsError()) {
+        // We may also want to continue parsing the other tokens to gather more errors.
+        return parse_attempt;
+      }  // else the value has been successfully stored into the map
+
+      assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
+      i += consumed_tokens;
+
+      // TODO: also handle ignoring arguments for backwards compatibility
+    }  // for
+
+    return CmdlineResult(CmdlineResult::kSuccess);
+  }
+
+  bool ignore_unrecognized_ = false;
+  std::vector<const char*> ignore_list_;
+  std::shared_ptr<SaveDestination> save_destination_;
+  std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+};
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+template <typename TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
+  return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
+      parent, parent.save_destination_);
+}
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
+    detail::CmdlineParseArgumentAny* arg) {
+  builder.AppendCompletedArgument(arg);
+}
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_PARSER_H_