Quellcode durchsuchen

cmArgumentParser: Model maybe-empty and non-empty lists with wrapper types

Previously bindings to `std::vector<std::string>` required at least one
value.  Some clients have been filtering `keywordsMissingValue` to
support keywords followed by empty lists.  Instead, require clients to
specify whether a keyword's list can be empty as part of the binding
type.
Brad King vor 3 Jahren
Ursprung
Commit
e6d1e29ffa

+ 2 - 1
Source/CTest/cmCTestCoverageCommand.h

@@ -11,6 +11,7 @@
 #include <cm/memory>
 #include <cm/optional>
 
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
 
@@ -44,5 +45,5 @@ protected:
   void BindArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  cm::optional<std::vector<std::string>> Labels;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels;
 };

+ 4 - 3
Source/CTest/cmCTestSubmitCommand.h

@@ -10,6 +10,7 @@
 
 #include <cm/optional>
 
+#include "cmArgumentParserTypes.h"
 #include "cmCTestHandlerCommand.h"
 
 class cmCommand;
@@ -50,7 +51,7 @@ protected:
   std::string RetryDelay;
   std::string SubmitURL;
 
-  cm::optional<std::vector<std::string>> Files;
-  std::vector<std::string> HttpHeaders;
-  cm::optional<std::vector<std::string>> Parts;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts;
 };

+ 2 - 1
Source/CTest/cmCTestUploadCommand.h

@@ -10,6 +10,7 @@
 
 #include <cm/memory>
 
+#include "cmArgumentParserTypes.h"
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
 
@@ -45,5 +46,5 @@ protected:
   void CheckArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  std::vector<std::string> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
 };

+ 10 - 1
Source/cmArgumentParser.cxx

@@ -4,6 +4,8 @@
 
 #include <algorithm>
 
+#include "cmArgumentParserTypes.h"
+
 namespace ArgumentParser {
 
 auto ActionMap::Emplace(cm::string_view name, Action action)
@@ -44,7 +46,14 @@ void Instance::Bind(std::string& val)
   this->ExpectValue = true;
 }
 
-void Instance::Bind(std::vector<std::string>& val)
+void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val)
+{
+  this->CurrentString = nullptr;
+  this->CurrentList = &val;
+  this->ExpectValue = false;
+}
+
+void Instance::Bind(NonEmpty<std::vector<std::string>>& val)
 {
   this->CurrentString = nullptr;
   this->CurrentList = &val;

+ 4 - 1
Source/cmArgumentParser.h

@@ -14,6 +14,8 @@
 #include <cm/string_view>
 #include <cmext/string_view>
 
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
+
 namespace ArgumentParser {
 
 class Instance;
@@ -37,7 +39,8 @@ public:
 
   void Bind(bool& val);
   void Bind(std::string& val);
-  void Bind(std::vector<std::string>& val);
+  void Bind(MaybeEmpty<std::vector<std::string>>& val);
+  void Bind(NonEmpty<std::vector<std::string>>& val);
   void Bind(std::vector<std::vector<std::string>>& val);
 
   // cm::optional<> records the presence the keyword to which it binds.

+ 19 - 0
Source/cmArgumentParserTypes.h

@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+namespace ArgumentParser {
+
+template <typename T>
+struct MaybeEmpty : public T
+{
+};
+
+template <typename T>
+struct NonEmpty : public T
+{
+};
+
+} // namespace ArgumentParser

+ 2 - 1
Source/cmCMakeLanguageCommand.cxx

@@ -14,6 +14,7 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmDependencyProvider.h"
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
@@ -237,7 +238,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
   struct SetProviderArgs
   {
     std::string Command;
-    std::vector<std::string> Methods;
+    ArgumentParser::NonEmpty<std::vector<std::string>> Methods;
   };
 
   auto const ArgsParser =

+ 3 - 2
Source/cmDefinePropertyCommand.cxx

@@ -8,6 +8,7 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmProperty.h"
@@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
   // Parse remaining arguments.
   bool inherited = false;
   std::string PropertyName;
-  std::vector<std::string> BriefDocs;
-  std::vector<std::string> FullDocs;
+  ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs;
+  ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs;
   std::string initializeFromVariable;
 
   cmArgumentParser<void> parser;

+ 2 - 1
Source/cmExportCommand.cxx

@@ -13,6 +13,7 @@
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmExperimental.h"
 #include "cmExportBuildAndroidMKGenerator.h"
@@ -58,7 +59,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   struct Arguments
   {
     std::string ExportSetName;
-    cm::optional<std::vector<std::string>> Targets;
+    cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
     std::string Namespace;
     std::string Filename;
     std::string AndroidMKFile;

+ 28 - 44
Source/cmFileCommand.cxx

@@ -30,6 +30,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmCMakePath.h"
 #include "cmCryptoHash.h"
 #include "cmELF.h"
@@ -2505,7 +2506,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     cm::optional<std::string> NewLineStyle;
     bool NoSourcePermissions = false;
     bool UseSourcePermissions = false;
-    std::vector<std::string> FilePermissions;
+    ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
   };
 
   static auto const parser =
@@ -3052,17 +3053,18 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
     std::string ConflictingDependenciesPrefix;
     std::string RPathPrefix;
     std::string BundleExecutable;
-    std::vector<std::string> Executables;
-    std::vector<std::string> Libraries;
-    std::vector<std::string> Directories;
-    std::vector<std::string> Modules;
-    std::vector<std::string> PreIncludeRegexes;
-    std::vector<std::string> PreExcludeRegexes;
-    std::vector<std::string> PostIncludeRegexes;
-    std::vector<std::string> PostExcludeRegexes;
-    std::vector<std::string> PostIncludeFiles;
-    std::vector<std::string> PostExcludeFiles;
-    std::vector<std::string> PostExcludeFilesStrict;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>>
+      PostExcludeFilesStrict;
   };
 
   static auto const parser =
@@ -3098,25 +3100,10 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  // Arguments that are allowed to be empty lists.  Keep entries sorted!
-  static const std::vector<cm::string_view> LIST_ARGS = {
-    "DIRECTORIES"_s,
-    "EXECUTABLES"_s,
-    "LIBRARIES"_s,
-    "MODULES"_s,
-    "POST_EXCLUDE_FILES"_s,
-    "POST_EXCLUDE_FILES_STRICT"_s,
-    "POST_EXCLUDE_REGEXES"_s,
-    "POST_INCLUDE_FILES"_s,
-    "POST_INCLUDE_REGEXES"_s,
-    "PRE_EXCLUDE_REGEXES"_s,
-    "PRE_INCLUDE_REGEXES"_s,
-  };
-  auto kwbegin = keywordsMissingValues.cbegin();
-  auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
-  if (kwend != kwbegin) {
-    status.SetError(cmStrCat("Keywords missing values:\n  ",
-                             cmJoin(cmMakeRange(kwbegin, kwend), "\n  ")));
+  if (!keywordsMissingValues.empty()) {
+    status.SetError(
+      cmStrCat("Keywords missing values:\n  ",
+               cmJoin(cmMakeRange(keywordsMissingValues), "\n  ")));
     cmSystemTools::SetFatalErrorOccurred();
     return false;
   }
@@ -3362,7 +3349,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
     std::string CompressionLevel;
     std::string MTime;
     bool Verbose = false;
-    std::vector<std::string> Paths;
+    // "PATHS" requires at least one value, but use a custom check below.
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
   };
 
   static auto const parser =
@@ -3393,7 +3381,6 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
                // value, but it has long been accidentally accepted without
                // one and treated as if an empty value were given.
                // Fixing this would require a policy.
-    "PATHS"_s, // "PATHS" is here only so we can issue a custom error below.
   };
   auto kwbegin = keywordsMissingValues.cbegin();
   auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
@@ -3496,7 +3483,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
     bool Verbose = false;
     bool ListOnly = false;
     std::string Destination;
-    std::vector<std::string> Patterns;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
     bool Touch = false;
   };
 
@@ -3520,13 +3507,10 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  // Arguments that are allowed to be empty lists.  Keep entries sorted!
-  static const std::vector<cm::string_view> LIST_ARGS = { "PATTERNS"_s };
-  auto kwbegin = keywordsMissingValues.cbegin();
-  auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
-  if (kwend != kwbegin) {
-    status.SetError(cmStrCat("Keywords missing values:\n  ",
-                             cmJoin(cmMakeRange(kwbegin, kwend), "\n  ")));
+  if (!keywordsMissingValues.empty()) {
+    status.SetError(
+      cmStrCat("Keywords missing values:\n  ",
+               cmJoin(cmMakeRange(keywordsMissingValues), "\n  ")));
     cmSystemTools::SetFatalErrorOccurred();
     return false;
   }
@@ -3620,9 +3604,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
 
   struct Arguments
   {
-    std::vector<std::string> Permissions;
-    std::vector<std::string> FilePermissions;
-    std::vector<std::string> DirectoryPermissions;
+    ArgumentParser::NonEmpty<std::vector<std::string>> Permissions;
+    ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
+    ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions;
   };
 
   static auto const parser =

+ 31 - 29
Source/cmInstallCommand.cxx

@@ -19,6 +19,7 @@
 #include "cmsys/Glob.hxx"
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmExperimental.h"
 #include "cmExportSet.h"
@@ -57,13 +58,13 @@ namespace {
 
 struct RuntimeDependenciesArgs
 {
-  std::vector<std::string> Directories;
-  std::vector<std::string> PreIncludeRegexes;
-  std::vector<std::string> PreExcludeRegexes;
-  std::vector<std::string> PostIncludeRegexes;
-  std::vector<std::string> PostExcludeRegexes;
-  std::vector<std::string> PostIncludeFiles;
-  std::vector<std::string> PostExcludeFiles;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
 };
 
 auto const RuntimeDependenciesArgHelper =
@@ -406,18 +407,18 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Archive;
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Object;
-    std::vector<std::string> Framework;
-    std::vector<std::string> Bundle;
-    std::vector<std::string> Includes;
-    std::vector<std::string> PrivateHeader;
-    std::vector<std::string> PublicHeader;
-    std::vector<std::string> Resource;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Object;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi;
     std::vector<std::vector<std::string>> FileSets;
-    std::vector<std::string> CxxModulesBmi;
   };
 
   static auto const argHelper =
@@ -441,9 +442,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   // now parse the generic args (i.e. the ones not specialized on LIBRARY/
   // ARCHIVE, RUNTIME etc. (see above)
   // These generic args also contain the targets and the export stuff
-  std::vector<std::string> targetList;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
   std::string exports;
-  cm::optional<std::vector<std::string>> runtimeDependenciesArgVector;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
+    runtimeDependenciesArgVector;
   std::string runtimeDependencySetArg;
   std::vector<std::string> unknownArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
@@ -1251,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Framework;
-    std::vector<std::string> Bundle;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
   };
 
   static auto const argHelper = cmArgumentParser<ArgVectors>{}
@@ -1268,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
 
   // now parse the generic args (i.e. the ones not specialized on LIBRARY,
   // RUNTIME etc. (see above)
-  std::vector<std::string> targetList;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
   std::string runtimeDependencySetArg;
   std::vector<std::string> unknownArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
@@ -1509,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args,
   // This is the FILES mode.
   bool programs = (args[0] == "PROGRAMS");
   cmInstallCommandArguments ica(helper.DefaultComponentName);
-  std::vector<std::string> files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> files;
   ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files);
   std::vector<std::string> unknownArgs;
   ica.Parse(args, &unknownArgs);
@@ -2140,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
   };
 
   static auto const argHelper = cmArgumentParser<ArgVectors>{}

+ 3 - 2
Source/cmInstallCommandArguments.h

@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 
 class cmInstallCommandArguments : public cmArgumentParser<void>
 {
@@ -44,8 +45,8 @@ private:
   std::string NamelinkComponent;
   bool ExcludeFromAll = false;
   std::string Rename;
-  std::vector<std::string> Permissions;
-  std::vector<std::string> Configurations;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations;
   bool Optional = false;
   bool NamelinkOnly = false;
   bool NamelinkSkip = false;

+ 3 - 1
Source/cmParseArgumentsCommand.cxx

@@ -10,6 +10,7 @@
 #include <cm/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -41,7 +42,8 @@ namespace {
 
 using options_map = std::map<std::string, bool>;
 using single_map = std::map<std::string, std::string>;
-using multi_map = std::map<std::string, std::vector<std::string>>;
+using multi_map =
+  std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>;
 using options_set = std::set<cm::string_view>;
 
 struct UserArgumentParser : public cmArgumentParser<void>

+ 3 - 2
Source/cmTargetSourcesCommand.cxx

@@ -9,6 +9,7 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
@@ -28,8 +29,8 @@ struct FileSetArgs
 {
   std::string Type;
   std::string FileSet;
-  std::vector<std::string> BaseDirs;
-  std::vector<std::string> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
 };
 
 auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>()

+ 12 - 5
Tests/CMakeLib/testArgumentParser.cxx

@@ -11,6 +11,7 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 
 namespace {
 
@@ -23,11 +24,12 @@ struct Result
   cm::optional<std::string> String2;
   cm::optional<std::string> String3;
 
-  std::vector<std::string> List1;
-  std::vector<std::string> List2;
-  cm::optional<std::vector<std::string>> List3;
-  cm::optional<std::vector<std::string>> List4;
-  cm::optional<std::vector<std::string>> List5;
+  ArgumentParser::NonEmpty<std::vector<std::string>> List1;
+  ArgumentParser::NonEmpty<std::vector<std::string>> List2;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List3;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List4;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List5;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> List6;
 
   std::vector<std::vector<std::string>> Multi1;
   std::vector<std::vector<std::string>> Multi2;
@@ -48,6 +50,7 @@ std::initializer_list<cm::string_view> const args = {
   "LIST_3", "foo",           // ... with continuation
   "LIST_4",                  // list arg missing values, presence captured
   // "LIST_5",               // list arg that is not present
+  "LIST_6",                  // list arg allowed to be empty
   "MULTI_2",                 // multi list with 0 lists
   "MULTI_3", "foo", "bar",   // multi list with first list with two elems
   "MULTI_3", "bar", "foo",   // multi list with second list with two elems
@@ -88,6 +91,8 @@ bool verifyResult(Result const& result,
   ASSERT_TRUE(result.List4);
   ASSERT_TRUE(result.List4->empty());
   ASSERT_TRUE(!result.List5);
+  ASSERT_TRUE(result.List6);
+  ASSERT_TRUE(result.List6->empty());
 
   ASSERT_TRUE(result.Multi1.empty());
   ASSERT_TRUE(result.Multi2.size() == 1);
@@ -122,6 +127,7 @@ bool testArgumentParserDynamic()
     .Bind("LIST_3"_s, result.List3)
     .Bind("LIST_4"_s, result.List4)
     .Bind("LIST_5"_s, result.List5)
+    .Bind("LIST_6"_s, result.List6)
     .Bind("MULTI_1"_s, result.Multi1)
     .Bind("MULTI_2"_s, result.Multi2)
     .Bind("MULTI_3"_s, result.Multi3)
@@ -145,6 +151,7 @@ bool testArgumentParserStatic()
       .Bind("LIST_3"_s, &Result::List3)
       .Bind("LIST_4"_s, &Result::List4)
       .Bind("LIST_5"_s, &Result::List5)
+      .Bind("LIST_6"_s, &Result::List6)
       .Bind("MULTI_1"_s, &Result::Multi1)
       .Bind("MULTI_2"_s, &Result::Multi2)
       .Bind("MULTI_3"_s, &Result::Multi3)