From 1ee5a4a548c62e5c382f3497be82aac36a99e485 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Thu, 16 Jun 2022 14:47:40 -0400
Subject: cmArgumentParser: Avoid allocating copies of keyword strings

---
 Source/CTest/cmCTestCoverageCommand.cxx        |  5 +-
 Source/CTest/cmCTestCoverageCommand.h          |  3 +-
 Source/CTest/cmCTestHandlerCommand.cxx         |  7 ++-
 Source/CTest/cmCTestHandlerCommand.h           |  4 +-
 Source/CTest/cmCTestSubmitCommand.cxx          |  7 ++-
 Source/CTest/cmCTestSubmitCommand.h            |  4 +-
 Source/CTest/cmCTestUploadCommand.cxx          |  3 +-
 Source/CTest/cmCTestUploadCommand.h            |  3 +-
 Source/cmArgumentParser.cxx                    |  8 +--
 Source/cmArgumentParser.h                      | 16 +++---
 Source/cmCMakeHostSystemInformationCommand.cxx |  2 +-
 Source/cmCMakePathCommand.cxx                  | 12 ++--
 Source/cmExecuteProcessCommand.cxx             |  6 +-
 Source/cmExportCommand.cxx                     |  3 +-
 Source/cmFileCommand.cxx                       | 78 +++++++++++++-------------
 Source/cmInstallCommand.cxx                    |  4 +-
 Source/cmParseArgumentsCommand.cxx             |  4 +-
 Tests/CMakeLib/testArgumentParser.cxx          |  9 +--
 18 files changed, 95 insertions(+), 83 deletions(-)

diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx
index 7432d08..9351b0b 100644
--- a/Source/CTest/cmCTestCoverageCommand.cxx
+++ b/Source/CTest/cmCTestCoverageCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <set>
 
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -19,10 +20,10 @@ void cmCTestCoverageCommand::BindArguments()
 }
 
 void cmCTestCoverageCommand::CheckArguments(
-  std::vector<std::string> const& keywords)
+  std::vector<cm::string_view> const& keywords)
 {
   this->LabelsMentioned =
-    !this->Labels.empty() || cm::contains(keywords, "LABELS");
+    !this->Labels.empty() || cm::contains(keywords, "LABELS"_s);
 }
 
 cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler()
diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h
index 9344852..20bb1a8 100644
--- a/Source/CTest/cmCTestCoverageCommand.h
+++ b/Source/CTest/cmCTestCoverageCommand.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include <cm/memory>
+#include <cm/string_view>
 
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
@@ -41,7 +42,7 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const& keywords) override;
+  void CheckArguments(std::vector<cm::string_view> const& keywords) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
   bool LabelsMentioned;
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx
index 5494d20..865e27b 100644
--- a/Source/CTest/cmCTestHandlerCommand.cxx
+++ b/Source/CTest/cmCTestHandlerCommand.cxx
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <sstream>
 
+#include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmCTest.h"
@@ -81,8 +82,8 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
 
   // Process input arguments.
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  std::vector<std::string> parsedKeywords;
+  std::vector<cm::string_view> keywordsMissingValue;
+  std::vector<cm::string_view> parsedKeywords;
   this->Parse(args, &unparsedArguments, &keywordsMissingValue,
               &parsedKeywords);
   this->CheckArguments(keywordsMissingValue);
@@ -242,6 +243,6 @@ void cmCTestHandlerCommand::BindArguments()
   this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex);
 }
 
-void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&)
+void cmCTestHandlerCommand::CheckArguments(std::vector<cm::string_view> const&)
 {
 }
diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h
index 756952d..4869deb 100644
--- a/Source/CTest/cmCTestHandlerCommand.h
+++ b/Source/CTest/cmCTestHandlerCommand.h
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmArgumentParser.h"
 #include "cmCTestCommand.h"
 
@@ -42,7 +44,7 @@ protected:
 
   // Command argument handling.
   virtual void BindArguments();
-  virtual void CheckArguments(std::vector<std::string> const& keywords);
+  virtual void CheckArguments(std::vector<cm::string_view> const& keywords);
 
   bool Append = false;
   bool Quiet = false;
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index a2dc615..288d5b4 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/string_view>
 #include <cm/vector>
 #include <cmext/algorithm>
 #include <cmext/string_view>
@@ -173,12 +174,12 @@ void cmCTestSubmitCommand::BindArguments()
 }
 
 void cmCTestSubmitCommand::CheckArguments(
-  std::vector<std::string> const& keywords)
+  std::vector<cm::string_view> const& keywords)
 {
   this->PartsMentioned =
-    !this->Parts.empty() || cm::contains(keywords, "PARTS");
+    !this->Parts.empty() || cm::contains(keywords, "PARTS"_s);
   this->FilesMentioned =
-    !this->Files.empty() || cm::contains(keywords, "FILES");
+    !this->Files.empty() || cm::contains(keywords, "FILES"_s);
 
   cm::erase_if(this->Parts, [this](std::string const& arg) -> bool {
     cmCTest::Part p = this->CTest->GetPartFromName(arg);
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index c5d11df..65f09c3 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmCTestHandlerCommand.h"
 
 class cmCommand;
@@ -35,7 +37,7 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const& keywords) override;
+  void CheckArguments(std::vector<cm::string_view> const& keywords) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
   bool CDashUpload = false;
diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx
index f86ee0d..8289dad 100644
--- a/Source/CTest/cmCTestUploadCommand.cxx
+++ b/Source/CTest/cmCTestUploadCommand.cxx
@@ -5,6 +5,7 @@
 #include <set>
 #include <sstream>
 
+#include <cm/string_view>
 #include <cm/vector>
 #include <cmext/string_view>
 
@@ -21,7 +22,7 @@ void cmCTestUploadCommand::BindArguments()
   this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
 }
 
-void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&)
+void cmCTestUploadCommand::CheckArguments(std::vector<cm::string_view> const&)
 {
   cm::erase_if(this->Files, [this](std::string const& arg) -> bool {
     if (!cmSystemTools::FileExists(arg)) {
diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h
index fe155f6..0058c31 100644
--- a/Source/CTest/cmCTestUploadCommand.h
+++ b/Source/CTest/cmCTestUploadCommand.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include <cm/memory>
+#include <cm/string_view>
 
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
@@ -42,7 +43,7 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const&) override;
+  void CheckArguments(std::vector<cm::string_view> const&) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
   std::vector<std::string> Files;
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx
index 4624f1c..bc91b01 100644
--- a/Source/cmArgumentParser.cxx
+++ b/Source/cmArgumentParser.cxx
@@ -60,17 +60,17 @@ void Instance::Bind(MultiStringList& val)
 
 void Instance::Consume(cm::string_view arg, void* result,
                        std::vector<std::string>* unparsedArguments,
-                       std::vector<std::string>* keywordsMissingValue,
-                       std::vector<std::string>* parsedKeywords)
+                       std::vector<cm::string_view>* keywordsMissingValue,
+                       std::vector<cm::string_view>* parsedKeywords)
 {
   auto const it = this->Bindings.Find(arg);
   if (it != this->Bindings.end()) {
     if (parsedKeywords != nullptr) {
-      parsedKeywords->emplace_back(arg);
+      parsedKeywords->emplace_back(it->first);
     }
     it->second(*this, result);
     if (this->ExpectValue && keywordsMissingValue != nullptr) {
-      keywordsMissingValue->emplace_back(arg);
+      keywordsMissingValue->emplace_back(it->first);
     }
     return;
   }
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
index 71ed844..93ef6c1 100644
--- a/Source/cmArgumentParser.h
+++ b/Source/cmArgumentParser.h
@@ -44,8 +44,8 @@ public:
 
   void Consume(cm::string_view arg, void* result,
                std::vector<std::string>* unparsedArguments,
-               std::vector<std::string>* keywordsMissingValue,
-               std::vector<std::string>* parsedKeywords);
+               std::vector<cm::string_view>* keywordsMissingValue,
+               std::vector<cm::string_view>* parsedKeywords);
 
 private:
   ActionMap const& Bindings;
@@ -79,8 +79,8 @@ public:
   template <typename Range>
   void Parse(Result& result, Range const& args,
              std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr,
-             std::vector<std::string>* parsedKeywords = nullptr) const
+             std::vector<cm::string_view>* keywordsMissingValue = nullptr,
+             std::vector<cm::string_view>* parsedKeywords = nullptr) const
   {
     ArgumentParser::Instance instance(this->Bindings);
     for (cm::string_view arg : args) {
@@ -92,8 +92,8 @@ public:
   template <typename Range>
   Result Parse(Range const& args,
                std::vector<std::string>* unparsedArguments = nullptr,
-               std::vector<std::string>* keywordsMissingValue = nullptr,
-               std::vector<std::string>* parsedKeywords = nullptr) const
+               std::vector<cm::string_view>* keywordsMissingValue = nullptr,
+               std::vector<cm::string_view>* parsedKeywords = nullptr) const
   {
     Result result;
     this->Parse(result, args, unparsedArguments, keywordsMissingValue,
@@ -120,8 +120,8 @@ public:
   template <typename Range>
   void Parse(Range const& args,
              std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr,
-             std::vector<std::string>* parsedKeywords = nullptr) const
+             std::vector<cm::string_view>* keywordsMissingValue = nullptr,
+             std::vector<cm::string_view>* parsedKeywords = nullptr) const
   {
     ArgumentParser::Instance instance(this->Bindings);
     for (cm::string_view arg : args) {
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 0750eea..033dd6d 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -491,7 +491,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
     .Bind("SEPARATOR"_s, &Arguments::Separator)
     .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable);
   std::vector<std::string> invalidArgs;
-  std::vector<std::string> keywordsMissingValue;
+  std::vector<cm::string_view> keywordsMissingValue;
 
   Arguments const arguments =
     parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue);
diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx
index bf94c2d..adcffa9 100644
--- a/Source/cmCMakePathCommand.cxx
+++ b/Source/cmCMakePathCommand.cxx
@@ -44,8 +44,8 @@ public:
 
   template <int Advance = 2>
   Result Parse(std::vector<std::string> const& args,
-               std::vector<std::string>* keywordsMissingValue = nullptr,
-               std::vector<std::string>* parsedKeywords = nullptr) const
+               std::vector<cm::string_view>* keywordsMissingValue = nullptr,
+               std::vector<cm::string_view>* parsedKeywords = nullptr) const
   {
     this->Inputs.clear();
 
@@ -89,11 +89,11 @@ public:
       args, &this->KeywordsMissingValue, &this->ParsedKeywords);
   }
 
-  const std::vector<std::string>& GetKeywordsMissingValue() const
+  const std::vector<cm::string_view>& GetKeywordsMissingValue() const
   {
     return this->KeywordsMissingValue;
   }
-  const std::vector<std::string>& GetParsedKeywords() const
+  const std::vector<cm::string_view>& GetParsedKeywords() const
   {
     return this->ParsedKeywords;
   }
@@ -121,8 +121,8 @@ public:
   }
 
 private:
-  mutable std::vector<std::string> KeywordsMissingValue;
-  mutable std::vector<std::string> ParsedKeywords;
+  mutable std::vector<cm::string_view> KeywordsMissingValue;
+  mutable std::vector<cm::string_view> ParsedKeywords;
 };
 
 struct OutputVariable
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 222ea80..af56e2d 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -95,13 +95,13 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
       .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
+  std::vector<cm::string_view> keywordsMissingValue;
   Arguments const arguments =
     parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
 
   if (!keywordsMissingValue.empty()) {
-    status.SetError(" called with no value for " +
-                    keywordsMissingValue.front() + ".");
+    status.SetError(cmStrCat(" called with no value for ",
+                             keywordsMissingValue.front(), "."));
     return false;
   }
   if (!unparsedArguments.empty()) {
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 63440a3..9a21a98 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -79,7 +80,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
 
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> keywordsMissingValue;
+  std::vector<cm::string_view> keywordsMissingValue;
   Arguments const arguments =
     parser.Parse(args, &unknownArgs, &keywordsMissingValue);
 
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 1cfe29c..6013bf8 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -958,8 +958,8 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args,
   bool removeEnvironmentRPath = false;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
+  std::vector<cm::string_view> missingArgs;
+  std::vector<cm::string_view> parsedArgs;
   parser.Bind("FILE"_s, file)
     .Bind("OLD_RPATH"_s, oldRPath)
     .Bind("NEW_RPATH"_s, newRPath)
@@ -1028,8 +1028,8 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args,
   std::string newRPath;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
+  std::vector<cm::string_view> missingArgs;
+  std::vector<cm::string_view> parsedArgs;
   parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath);
   parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
                &parsedArgs);
@@ -1087,7 +1087,7 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
   std::string file;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
+  std::vector<cm::string_view> missingArgs;
   parser.Bind("FILE"_s, file);
   parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs);
   if (!unknownArgs.empty()) {
@@ -1138,8 +1138,8 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args,
   std::string rpath;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
+  std::vector<cm::string_view> missingArgs;
+  std::vector<cm::string_view> parsedArgs;
   parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath);
   parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
                &parsedArgs);
@@ -1261,8 +1261,8 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
       .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  std::vector<std::string> parsedKeywords;
+  std::vector<cm::string_view> keywordsMissingValue;
+  std::vector<cm::string_view> parsedKeywords;
   auto arguments =
     parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments,
                  &keywordsMissingValue, &parsedKeywords);
@@ -2521,8 +2521,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
       .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValues;
-  std::vector<std::string> parsedKeywords;
+  std::vector<cm::string_view> keywordsMissingValues;
+  std::vector<cm::string_view> parsedKeywords;
   Arguments const arguments =
     parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments,
                  &keywordsMissingValues, &parsedKeywords);
@@ -2578,7 +2578,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
   }
 
   const bool inputIsContent = parsedKeywords[1] != "INPUT"_s;
-  if (inputIsContent && parsedKeywords[1] != "CONTENT") {
+  if (inputIsContent && parsedKeywords[1] != "CONTENT"_s) {
     status.SetError("Unknown argument to GENERATE subcommand.");
   }
 
@@ -3102,7 +3102,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
       .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
   auto parsedArgs =
     parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
                  &keywordsMissingValues);
@@ -3114,18 +3114,18 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
   }
 
   // Arguments that are allowed to be empty lists.  Keep entries sorted!
-  const std::vector<std::string> LIST_ARGS = {
-    "DIRECTORIES",
-    "EXECUTABLES",
-    "LIBRARIES",
-    "MODULES",
-    "POST_EXCLUDE_FILES",
-    "POST_EXCLUDE_FILES_STRICT",
-    "POST_EXCLUDE_REGEXES",
-    "POST_INCLUDE_FILES",
-    "POST_INCLUDE_REGEXES",
-    "PRE_EXCLUDE_REGEXES",
-    "PRE_INCLUDE_REGEXES",
+  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);
@@ -3251,8 +3251,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
       .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingArguments;
-  std::vector<std::string> parsedKeywords;
+  std::vector<cm::string_view> keywordsMissingArguments;
+  std::vector<cm::string_view> parsedKeywords;
   auto parsedArgs =
     parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
                  &keywordsMissingArguments, &parsedKeywords);
@@ -3265,7 +3265,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" };
+  std::vector<cm::string_view> mandatoryOptions{ "OUTPUT"_s, "CONTENT"_s };
   for (auto const& e : mandatoryOptions) {
     const bool optionHasNoValue =
       std::find(keywordsMissingArguments.begin(),
@@ -3392,7 +3392,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
       .Bind("PATHS"_s, &Arguments::Paths);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
   auto parsedArgs =
     parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
                  &keywordsMissingValues);
@@ -3404,12 +3404,12 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
   }
 
   // Arguments that are allowed to be empty lists.  Keep entries sorted!
-  const std::vector<std::string> LIST_ARGS = {
-    "MTIME", // "MTIME" should not be in this list because it requires one
-             // 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", // "PATHS" is here only so we can issue a custom error below.
+  static const std::vector<cm::string_view> LIST_ARGS = {
+    "MTIME"_s, // "MTIME" should not be in this list because it requires one
+               // 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);
@@ -3525,7 +3525,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
                                .Bind("TOUCH"_s, &Arguments::Touch);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
   auto parsedArgs =
     parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
                  &keywordsMissingValues);
@@ -3537,7 +3537,7 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
   }
 
   // Arguments that are allowed to be empty lists.  Keep entries sorted!
-  const std::vector<std::string> LIST_ARGS = { "PATTERNS" };
+  static const std::vector<cm::string_view> LIST_ARGS = { "PATTERNS"_s };
   auto kwbegin = keywordsMissingValues.cbegin();
   auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
   if (kwend != kwbegin) {
@@ -3648,7 +3648,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
       .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
 
   std::vector<std::string> pathEntries;
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
   Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1),
                                       &pathEntries, &keywordsMissingValues);
 
@@ -3672,7 +3672,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
 
   if (!keywordsMissingValues.empty()) {
     for (const auto& i : keywordsMissingValues) {
-      status.SetError(i + " is not given any arguments");
+      status.SetError(cmStrCat(i, " is not given any arguments"));
       cmSystemTools::SetFatalErrorOccurred();
     }
     return false;
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 7ca5b23..46c2d33 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -439,7 +439,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   std::vector<std::string> runtimeDependenciesArgVector;
   std::string runtimeDependencySetArg;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> parsedArgs;
+  std::vector<cm::string_view> parsedArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
   genericArgs.Bind("TARGETS"_s, targetList);
   genericArgs.Bind("EXPORT"_s, exports);
@@ -2106,7 +2106,7 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
   // These generic args also contain the runtime dependency set
   std::string runtimeDependencySetArg;
   std::vector<std::string> runtimeDependencyArgVector;
-  std::vector<std::string> parsedArgs;
+  std::vector<cm::string_view> parsedArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
   genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg);
   genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr,
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 95f3e7e..5ea35e8 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -42,7 +42,7 @@ 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 options_set = std::set<std::string>;
+using options_set = std::set<cm::string_view>;
 
 struct UserArgumentParser : public cmArgumentParser<void>
 {
@@ -208,7 +208,7 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args,
     }
   }
 
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
 
   parser.Parse(list, &unparsed, &keywordsMissingValues);
 
diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx
index 965690c..954c96f 100644
--- a/Tests/CMakeLib/testArgumentParser.cxx
+++ b/Tests/CMakeLib/testArgumentParser.cxx
@@ -47,11 +47,12 @@ std::initializer_list<cm::string_view> const args = {
 
 bool verifyResult(Result const& result,
                   std::vector<std::string> const& unparsedArguments,
-                  std::vector<std::string> const& keywordsMissingValue)
+                  std::vector<cm::string_view> const& keywordsMissingValue)
 {
   static std::vector<std::string> const foobar = { "foo", "bar" };
   static std::vector<std::string> const barfoo = { "bar", "foo" };
-  static std::vector<std::string> const missing = { "STRING_1", "LIST_1" };
+  static std::vector<cm::string_view> const missing = { "STRING_1"_s,
+                                                        "LIST_1"_s };
 
 #define ASSERT_TRUE(x)                                                        \
   do {                                                                        \
@@ -89,7 +90,7 @@ bool testArgumentParserDynamic()
 {
   Result result;
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
+  std::vector<cm::string_view> keywordsMissingValue;
 
   cmArgumentParser<void>{}
     .Bind("OPTION_1"_s, result.Option1)
@@ -123,7 +124,7 @@ bool testArgumentParserStatic()
       .Bind("MULTI_3"_s, &Result::Multi3);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
+  std::vector<cm::string_view> keywordsMissingValue;
   Result const result =
     parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
 
-- 
cgit v0.12