Browse Source

Merge topic 'astgrep-has-prefix-suffix-simplifications'

366b25e62e ast-grep: simplify `cmHasLiteral{Suf,Pre}fix` with char literals
9945be27af Utilities/ast-grep: add rules for `cmHasLiteral{Suf,Pre}fix` with char needles
eab4c033ea ast-grep: simplify `cmHas{Suf,Pre}fix` with character needles
d9ee22f331 Utilities/ast-grep: add rules for `cmHas{Suf,Pre}fix` for character needles
aa477d8768 ast-grep: simplify `cmStrCat` calls with one-char strings
514cbbc76e ast-grep: combine string literal arguments in `cmStrCat`

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !11366
Brad King 1 week ago
parent
commit
aa39871396
41 changed files with 346 additions and 37 deletions
  1. 1 1
      Source/CPack/cmCPackArchiveGenerator.cxx
  2. 1 1
      Source/CPack/cmCPackGenerator.cxx
  3. 1 1
      Source/CTest/cmParseGTMCoverage.cxx
  4. 1 1
      Source/QtDialog/CMakeSetup.cxx
  5. 4 2
      Source/cmBinUtilsLinker.cxx
  6. 1 1
      Source/cmCTest.cxx
  7. 1 1
      Source/cmCustomCommandGenerator.cxx
  8. 4 4
      Source/cmFastbuildNormalTargetGenerator.cxx
  9. 1 1
      Source/cmFileAPICodemodel.cxx
  10. 2 1
      Source/cmGeneratorExpressionNode.cxx
  11. 1 1
      Source/cmGlobalFastbuildGenerator.cxx
  12. 1 1
      Source/cmGlobalGenerator.cxx
  13. 3 3
      Source/cmGlobalNinjaGenerator.cxx
  14. 1 1
      Source/cmGlobalVisualStudioGenerator.cxx
  15. 1 1
      Source/cmLinkLineDeviceComputer.cxx
  16. 1 1
      Source/cmLocalNinjaGenerator.cxx
  17. 1 1
      Source/cmMakefileTargetGenerator.cxx
  18. 1 1
      Source/cmNinjaTargetGenerator.cxx
  19. 1 1
      Source/cmPackageInfoReader.cxx
  20. 1 1
      Source/cmProjectCommand.cxx
  21. 1 1
      Source/cmSetCommand.cxx
  22. 1 1
      Source/cmSystemTools.cxx
  23. 1 1
      Source/cmTargetLinkLibrariesCommand.cxx
  24. 1 1
      Source/cmTargetPrecompileHeadersCommand.cxx
  25. 1 1
      Source/cmUnsetCommand.cxx
  26. 1 1
      Source/cmake.cxx
  27. 5 5
      Source/cmcmd.cxx
  28. 23 0
      Utilities/ast-grep/rule-tests/__snapshots__/cmhasliteralprefix-char-snapshot.yml
  29. 23 0
      Utilities/ast-grep/rule-tests/__snapshots__/cmhasliteralsuffix-char-snapshot.yml
  30. 47 0
      Utilities/ast-grep/rule-tests/__snapshots__/cmhasprefix-char-snapshot.yml
  31. 47 0
      Utilities/ast-grep/rule-tests/__snapshots__/cmhassuffix-char-snapshot.yml
  32. 8 0
      Utilities/ast-grep/rule-tests/cmhasliteralprefix-char.yml
  33. 8 0
      Utilities/ast-grep/rule-tests/cmhasliteralsuffix-char.yml
  34. 9 0
      Utilities/ast-grep/rule-tests/cmhasprefix-char.yml
  35. 9 0
      Utilities/ast-grep/rule-tests/cmhassuffix-char.yml
  36. 27 0
      Utilities/ast-grep/rules/cmhasliteralprefix-char.yml
  37. 27 0
      Utilities/ast-grep/rules/cmhasliteralsuffix-char.yml
  38. 34 0
      Utilities/ast-grep/rules/cmhasprefix-char.yml
  39. 34 0
      Utilities/ast-grep/rules/cmhassuffix-char.yml
  40. 5 0
      Utilities/ast-grep/utils/cmhasprefix-call.yml
  41. 5 0
      Utilities/ast-grep/utils/cmhassuffix-call.yml

+ 1 - 1
Source/CPack/cmCPackArchiveGenerator.cxx

@@ -233,7 +233,7 @@ int cmCPackArchiveGenerator::InitializeInternal()
   cmValue newExtensionValue = this->GetOption("CPACK_ARCHIVE_FILE_EXTENSION");
   if (!newExtensionValue.IsEmpty()) {
     std::string newExtension = *newExtensionValue;
-    if (!cmHasLiteralPrefix(newExtension, ".")) {
+    if (!cmHasPrefix(newExtension, '.')) {
       newExtension = cmStrCat('.', newExtension);
     }
     cmCPackLogger(cmCPackLog::LOG_DEBUG,

+ 1 - 1
Source/CPack/cmCPackGenerator.cxx

@@ -834,7 +834,7 @@ int cmCPackGenerator::InstallCMakeProject(
     // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory
     // exists:
     //
-    if (cmHasLiteralPrefix(dir, "/")) {
+    if (cmHasPrefix(dir, '/')) {
       dir = tempInstallDirectory + dir;
     } else {
       dir = tempInstallDirectory + "/" + dir;

+ 1 - 1
Source/CTest/cmParseGTMCoverage.cxx

@@ -85,7 +85,7 @@ bool cmParseGTMCoverage::ReadMCovFile(char const* file)
     }
     // Find the full path to the file
     bool found = this->FindMumpsFile(routine, filepath);
-    if (!found && cmHasLiteralSuffix(routine, "%")) {
+    if (!found && cmHasSuffix(routine, '%')) {
       routine.erase(0, 1);
       found = this->FindMumpsFile(routine, filepath);
     }

+ 1 - 1
Source/QtDialog/CMakeSetup.cxx

@@ -287,7 +287,7 @@ static bool cmOSXInstall(std::string const& dir, std::string const& tool)
 
 static int cmOSXInstall(std::string dir)
 {
-  if (!cmHasLiteralSuffix(dir, "/")) {
+  if (!cmHasSuffix(dir, '/')) {
     dir += "/";
   }
   return (cmOSXInstall(dir, cmSystemTools::GetCMakeCommand()) &&

+ 4 - 2
Source/cmBinUtilsLinker.cxx

@@ -36,8 +36,10 @@ void cmBinUtilsLinker::NormalizePath(std::string& path) const
   if (policy == cmPolicies::WARN) {
     this->Archive->GetMakefile()->IssueMessage(
       MessageType::AUTHOR_WARNING,
-      cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0207), '\n',
-               "Path\n  \"", path,
+      cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0207),
+               "\n"
+               "Path\n  \"",
+               path,
                "\"\n"
                "would be converted to\n  \"",
                normalizedPath, "\"\n"));

+ 1 - 1
Source/cmCTest.cxx

@@ -2531,7 +2531,7 @@ int cmCTest::Run(std::vector<std::string> const& args)
         this->Impl->BuildAndTest.TestCommandArgs.emplace_back(args[i]);
       }
     }
-    if (!matched && cmHasLiteralPrefix(arg, "-") &&
+    if (!matched && cmHasPrefix(arg, '-') &&
         !cmHasLiteralPrefix(arg, "--preset")) {
       cmSystemTools::Error(cmStrCat("Unknown argument: ", arg));
       cmSystemTools::Error("Run 'ctest --help' for all supported options.");

+ 1 - 1
Source/cmCustomCommandGenerator.cxx

@@ -53,7 +53,7 @@ std::string EvaluateSplitConfigGenex(
         ++pos;
         continue;
       }
-      if (cmHasLiteralPrefix(cur, ">")) {
+      if (cmHasPrefix(cur, '>')) {
         --nestingLevel;
         if (nestingLevel == 0) {
           ++pos;

+ 4 - 4
Source/cmFastbuildNormalTargetGenerator.cxx

@@ -185,7 +185,7 @@ void cmFastbuildNormalTargetGenerator::GetLinkerExecutableAndArgs(
   if (iter != compilers.end()) {
     LogMessage("Linker launcher: " + iter->first);
     outLinkerExecutable = iter->second.Executable;
-    outLinkerArgs = cmStrCat(iter->second.Args, " ", command);
+    outLinkerArgs = cmStrCat(iter->second.Args, ' ', command);
   } else {
     SplitLinkerFromArgs(command, outLinkerExecutable, outLinkerArgs);
   }
@@ -359,7 +359,7 @@ void cmFastbuildNormalTargetGenerator::ApplyLinkRuleLauncher(
     this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", Config);
   if (cmNonempty(val)) {
     LogMessage("RULE_LAUNCH_LINK: " + val);
-    command = cmStrCat(val, " ", command);
+    command = cmStrCat(val, ' ', command);
   }
 }
 
@@ -550,7 +550,7 @@ void cmFastbuildNormalTargetGenerator::GenerateModuleDefinitionInfo(
     execNode.Name = target.Name + "-def-files";
     execNode.ExecExecutable = cmSystemTools::GetCMakeCommand();
     execNode.ExecArguments =
-      cmStrCat("-E __create_def ", FASTBUILD_2_INPUT_PLACEHOLDER, " ",
+      cmStrCat("-E __create_def ", FASTBUILD_2_INPUT_PLACEHOLDER, ' ',
                FASTBUILD_1_INPUT_PLACEHOLDER);
     std::string const obj_list_file = mdi->DefFile + ".objs";
 
@@ -1492,7 +1492,7 @@ void cmFastbuildNormalTargetGenerator::GenerateObjects(FastbuildTarget& target)
 
   for (auto& val : nodesPermutations) {
     auto& objectListNode = val.second;
-    objectListNode.Name = cmStrCat(objectListNode.Name, "_", ++groupNameCount);
+    objectListNode.Name = cmStrCat(objectListNode.Name, '_', ++groupNameCount);
     LogMessage(cmStrCat("ObjectList name: ", objectListNode.Name));
   }
   std::vector<FastbuildObjectListNode>& objects = target.ObjectListNodes;

+ 1 - 1
Source/cmFileAPICodemodel.cxx

@@ -1067,7 +1067,7 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen)
     installer["destination"] = installDir->GetDestination(this->Config);
     Json::Value paths = Json::arrayValue;
     for (std::string const& dir : dirs) {
-      if (cmHasLiteralSuffix(dir, "/")) {
+      if (cmHasSuffix(dir, '/')) {
         paths.append(this->DumpInstallerPath(
           this->TopSource, dir.substr(0, dir.size() - 1), "."));
       } else {

+ 2 - 1
Source/cmGeneratorExpressionNode.cxx

@@ -5053,7 +5053,8 @@ struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
         postfix != Postfix::Unspecified) {
       eval->Context.LG->GetCMakeInstance()->IssueMessage(
         MessageType::AUTHOR_WARNING,
-        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0202), '\n',
+        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0202),
+                 "\n"
                  "\"POSTFIX\" option is recognized only when the policy is "
                  "set to NEW. Since the policy is not set, the OLD behavior "
                  "will be used."),

+ 1 - 1
Source/cmGlobalFastbuildGenerator.cxx

@@ -1631,7 +1631,7 @@ void cmGlobalFastbuildGenerator::WriteSolution()
         folderName,
         { { "Path", Quote(pathToFolder) },
           { "Projects",
-            cmStrCat("{", cmJoin(Wrap(projectsInFolder), ","), "}") } },
+            cmStrCat('{', cmJoin(Wrap(projectsInFolder), ","), '}') } },
         1);
       folders.emplace_back(std::move(folderName));
     }

+ 1 - 1
Source/cmGlobalGenerator.cxx

@@ -1185,7 +1185,7 @@ void cmGlobalGenerator::SetLanguageEnabledMaps(std::string const& l,
     std::string outputExtension = *p;
     this->LanguageToOutputExtension[l] = outputExtension;
     this->OutputExtensions[outputExtension] = outputExtension;
-    if (cmHasPrefix(outputExtension, ".")) {
+    if (cmHasPrefix(outputExtension, '.')) {
       outputExtension = outputExtension.substr(1);
       this->OutputExtensions[outputExtension] = outputExtension;
     }

+ 3 - 3
Source/cmGlobalNinjaGenerator.cxx

@@ -2511,7 +2511,7 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
     }
 
     dir_top_bld = tdi["dir-top-bld"].asString();
-    if (!dir_top_bld.empty() && !cmHasLiteralSuffix(dir_top_bld, "/")) {
+    if (!dir_top_bld.empty() && !cmHasSuffix(dir_top_bld, '/')) {
       dir_top_bld += '/';
     }
 
@@ -2524,7 +2524,7 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
 
     Json::Value const& tdi_module_dir = tdi["module-dir"];
     module_dir = tdi_module_dir.asString();
-    if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
+    if (!module_dir.empty() && !cmHasSuffix(module_dir, '/')) {
       module_dir += '/';
     }
 
@@ -3021,7 +3021,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
   }
 
   std::string module_dir = tdi["module-dir"].asString();
-  if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
+  if (!module_dir.empty() && !cmHasSuffix(module_dir, '/')) {
     module_dir += '/';
   }
   std::vector<std::string> linked_target_dirs;

+ 1 - 1
Source/cmGlobalVisualStudioGenerator.cxx

@@ -998,7 +998,7 @@ cm::VS::Solution cmGlobalVisualStudioGenerator::CreateSolution(
         root->MaybeRelativeToCurBinDir(lg->GetCurrentBinaryDirectory());
       if (dir == "."_s) {
         dir.clear();
-      } else if (!cmHasLiteralSuffix(dir, "/")) {
+      } else if (!cmHasSuffix(dir, '/')) {
         dir += "/";
       }
 

+ 1 - 1
Source/cmLinkLineDeviceComputer.cxx

@@ -45,7 +45,7 @@ static bool cmLinkItemValidForDevice(std::string const& item)
   // * '-pthread' => drop
   // * '-a' => drop
   // * '-framework Name' (as one string) => drop
-  return (!cmHasLiteralPrefix(item, "-") || //
+  return (!cmHasPrefix(item, '-') ||        //
           cmHasLiteralPrefix(item, "-l") || //
           cmHasLiteralPrefix(item, "-L") || //
           cmHasLiteralPrefix(item, "--library"));

+ 1 - 1
Source/cmLocalNinjaGenerator.cxx

@@ -489,7 +489,7 @@ bool RuleNeedsCMD(std::string const& cmd)
   auto it = std::find_if(args.cbegin(), args.cend(),
                          [](std::string const& arg) -> bool {
                            // FIXME: Detect more windows shell operators.
-                           return cmHasLiteralPrefix(arg, ">");
+                           return cmHasPrefix(arg, '>');
                          });
   return it != args.cend();
 }

+ 1 - 1
Source/cmMakefileTargetGenerator.cxx

@@ -919,7 +919,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
       cmOutputConverter::SHELL);
 
     if (this->LocalGenerator->IsMinGWMake() &&
-        cmHasLiteralSuffix(targetOutPathCompilePDB, "\\")) {
+        cmHasSuffix(targetOutPathCompilePDB, '\\')) {
       // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c'
       // (but 'a\ b "c"' as 'a\', 'b', and 'c'!).  Workaround this by
       // avoiding a trailing backslash in the argument.

+ 1 - 1
Source/cmNinjaTargetGenerator.cxx

@@ -456,7 +456,7 @@ std::string cmNinjaTargetGenerator::GetPreprocessedFilePath(
 {
   // Choose an extension to compile already-preprocessed source.
   std::string ppExt = source->GetExtension();
-  if (cmHasLiteralPrefix(ppExt, "F")) {
+  if (cmHasPrefix(ppExt, 'F')) {
     // Some Fortran compilers automatically enable preprocessing for
     // upper-case extensions.  Since the source is already preprocessed,
     // use a lower-case extension.

+ 1 - 1
Source/cmPackageInfoReader.cxx

@@ -223,7 +223,7 @@ std::vector<std::string> ReadList(Json::Value const& data, char const* key)
 std::string NormalizeTargetName(std::string const& name,
                                 std::string const& context)
 {
-  if (cmHasLiteralPrefix(name, ":")) {
+  if (cmHasPrefix(name, ':')) {
     return cmStrCat(context, ':', name);
   }
 

+ 1 - 1
Source/cmProjectCommand.cxx

@@ -273,7 +273,7 @@ bool cmProjectCommand(std::vector<std::string> const& args,
   // Note, this intentionally doesn't touch cache variables as the legacy
   // behavior did not modify cache
   auto checkAndClearVariables = [&](cm::string_view var) {
-    std::vector<std::string> vv = { "PROJECT_", cmStrCat(projectName, "_") };
+    std::vector<std::string> vv = { "PROJECT_", cmStrCat(projectName, '_') };
     if (mf.IsRootMakefile()) {
       vv.push_back("CMAKE_PROJECT_");
     }

+ 1 - 1
Source/cmSetCommand.cxx

@@ -81,7 +81,7 @@ bool cmSetCommand(std::vector<std::string> const& args,
 
   // watch for CACHE{} signature
   if (cmHasLiteralPrefix(variable, "CACHE{") && variable.size() > 7 &&
-      cmHasLiteralSuffix(variable, "}")) {
+      cmHasSuffix(variable, '}')) {
     // what is the variable name
     auto const& varName = variable.substr(6, variable.size() - 7);
     // VALUE handling

+ 1 - 1
Source/cmSystemTools.cxx

@@ -642,7 +642,7 @@ std::vector<std::string> cmSystemTools::HandleResponseFile(
 {
   std::vector<std::string> arg_full;
   for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
-    if (cmHasLiteralPrefix(arg, "@")) {
+    if (cmHasPrefix(arg, '@')) {
       cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
       if (!responseFile) {
         std::string error = cmStrCat("failed to open for reading (",

+ 1 - 1
Source/cmTargetLinkLibrariesCommand.cxx

@@ -283,7 +283,7 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
           if (cmHasLiteralPrefix(cur, "$<")) {
             ++genexNesting;
             ++pos;
-          } else if (genexNesting > 0 && cmHasLiteralPrefix(cur, ">")) {
+          } else if (genexNesting > 0 && cmHasPrefix(cur, '>')) {
             --genexNesting;
           }
         }

+ 1 - 1
Source/cmTargetPrecompileHeadersCommand.cxx

@@ -28,7 +28,7 @@ std::vector<std::string> ConvertToAbsoluteContent(
     // Use '<foo.h>' and '"foo.h"' includes and absolute paths as-is.
     // Interpret relative paths with respect to the source directory.
     // If the path starts in a generator expression, assume it is absolute.
-    if (cmHasLiteralPrefix(src, "<") || cmHasLiteralPrefix(src, "\"") ||
+    if (cmHasPrefix(src, '<') || cmHasPrefix(src, '"') ||
         cmSystemTools::FileIsFullPath(src) ||
         cmGeneratorExpression::Find(src) == 0) {
       absoluteSrc = src;

+ 1 - 1
Source/cmUnsetCommand.cxx

@@ -30,7 +30,7 @@ bool cmUnsetCommand(std::vector<std::string> const& args,
   }
   // unset(CACHE{VAR})
   if (cmHasLiteralPrefix(variable, "CACHE{") && variable.size() > 7 &&
-      cmHasLiteralSuffix(variable, "}")) {
+      cmHasSuffix(variable, '}')) {
     // get the variable name
     auto const& varName = variable.substr(6, variable.size() - 7);
     status.GetMakefile().RemoveCacheDefinition(varName);

+ 1 - 1
Source/cmake.cxx

@@ -1483,7 +1483,7 @@ void cmake::SetArgs(std::vector<std::string> const& args)
     if (!parsedCorrectly) {
       cmSystemTools::Error("Run 'cmake --help' for all supported options.");
       exit(1);
-    } else if (!matched && cmHasLiteralPrefix(arg, "-")) {
+    } else if (!matched && cmHasPrefix(arg, '-')) {
       possibleUnknownArg = arg;
     } else if (!matched) {
       bool parsedDirectory = this->SetDirectoriesFromFile(arg);

+ 5 - 5
Source/cmcmd.cxx

@@ -180,7 +180,7 @@ bool cmTarFilesFrom(std::string const& file, std::vector<std::string>& files)
     }
     if (cmHasLiteralPrefix(line, "--add-file=")) {
       files.push_back(line.substr(11));
-    } else if (cmHasLiteralPrefix(line, "-")) {
+    } else if (cmHasPrefix(line, '-')) {
       cmSystemTools::Error(cmStrCat("-E tar --files-from='", file,
                                     "' file invalid line:\n", line, '\n'));
       return false;
@@ -1084,7 +1084,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
       bool doing_options = true;
       bool at_least_one_file = false;
       for (auto const& arg : cmMakeRange(args).advance(2)) {
-        if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+        if (doing_options && cmHasPrefix(arg, '-')) {
           if (arg == "--") {
             doing_options = false;
           }
@@ -1239,7 +1239,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
           // Destroy console buffers to drop cout/cerr encoding transform.
           console.reset();
           cmCatFile(arg);
-        } else if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+        } else if (doing_options && cmHasPrefix(arg, '-')) {
           if (arg == "--") {
             doing_options = false;
           } else {
@@ -2416,13 +2416,13 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
   // Parse our own arguments.
   std::string intDir;
   auto arg = argBeg;
-  while (arg != argEnd && cmHasLiteralPrefix(*arg, "-")) {
+  while (arg != argEnd && cmHasPrefix(*arg, '-')) {
     if (*arg == "--") {
       ++arg;
       break;
     }
     if (*arg == "--manifests") {
-      for (++arg; arg != argEnd && !cmHasLiteralPrefix(*arg, "-"); ++arg) {
+      for (++arg; arg != argEnd && !cmHasPrefix(*arg, '-'); ++arg) {
         this->UserManifests.push_back(*arg);
       }
     } else if (cmHasLiteralPrefix(*arg, "--intdir=")) {

+ 23 - 0
Utilities/ast-grep/rule-tests/__snapshots__/cmhasliteralprefix-char-snapshot.yml

@@ -0,0 +1,23 @@
+id: cmhasliteralprefix-char
+snapshots:
+  cmHasLiteralPrefix(haystack, "'"):
+    fixed: cmHasPrefix(haystack, '\'')
+    labels:
+    - source: cmHasLiteralPrefix(haystack, "'")
+      style: primary
+      start: 0
+      end: 33
+  cmHasLiteralPrefix(haystack, "\n"):
+    fixed: cmHasPrefix(haystack, '\n')
+    labels:
+    - source: cmHasLiteralPrefix(haystack, "\n")
+      style: primary
+      start: 0
+      end: 34
+  cmHasLiteralPrefix(haystack, "a"):
+    fixed: cmHasPrefix(haystack, 'a')
+    labels:
+    - source: cmHasLiteralPrefix(haystack, "a")
+      style: primary
+      start: 0
+      end: 33

+ 23 - 0
Utilities/ast-grep/rule-tests/__snapshots__/cmhasliteralsuffix-char-snapshot.yml

@@ -0,0 +1,23 @@
+id: cmhasliteralsuffix-char
+snapshots:
+  cmHasLiteralSuffix(haystack, "'"):
+    fixed: cmHasSuffix(haystack, '\'')
+    labels:
+    - source: cmHasLiteralSuffix(haystack, "'")
+      style: primary
+      start: 0
+      end: 33
+  cmHasLiteralSuffix(haystack, "\n"):
+    fixed: cmHasSuffix(haystack, '\n')
+    labels:
+    - source: cmHasLiteralSuffix(haystack, "\n")
+      style: primary
+      start: 0
+      end: 34
+  cmHasLiteralSuffix(haystack, "a"):
+    fixed: cmHasSuffix(haystack, 'a')
+    labels:
+    - source: cmHasLiteralSuffix(haystack, "a")
+      style: primary
+      start: 0
+      end: 33

+ 47 - 0
Utilities/ast-grep/rule-tests/__snapshots__/cmhasprefix-char-snapshot.yml

@@ -0,0 +1,47 @@
+id: cmhasprefix-char
+snapshots:
+  cmHasPrefix(haystack, "'"):
+    fixed: cmHasPrefix(haystack, '\'')
+    labels:
+    - source: '"''"'
+      style: primary
+      start: 22
+      end: 25
+    - source: cmHasPrefix(haystack, "'")
+      style: secondary
+      start: 0
+      end: 26
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21
+  cmHasPrefix(haystack, "\n"):
+    fixed: cmHasPrefix(haystack, '\n')
+    labels:
+    - source: '"\n"'
+      style: primary
+      start: 22
+      end: 26
+    - source: cmHasPrefix(haystack, "\n")
+      style: secondary
+      start: 0
+      end: 27
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21
+  cmHasPrefix(haystack, "a"):
+    fixed: cmHasPrefix(haystack, 'a')
+    labels:
+    - source: '"a"'
+      style: primary
+      start: 22
+      end: 25
+    - source: cmHasPrefix(haystack, "a")
+      style: secondary
+      start: 0
+      end: 26
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21

+ 47 - 0
Utilities/ast-grep/rule-tests/__snapshots__/cmhassuffix-char-snapshot.yml

@@ -0,0 +1,47 @@
+id: cmhassuffix-char
+snapshots:
+  cmHasSuffix(haystack, "'"):
+    fixed: cmHasSuffix(haystack, '\'')
+    labels:
+    - source: '"''"'
+      style: primary
+      start: 22
+      end: 25
+    - source: cmHasSuffix(haystack, "'")
+      style: secondary
+      start: 0
+      end: 26
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21
+  cmHasSuffix(haystack, "\n"):
+    fixed: cmHasSuffix(haystack, '\n')
+    labels:
+    - source: '"\n"'
+      style: primary
+      start: 22
+      end: 26
+    - source: cmHasSuffix(haystack, "\n")
+      style: secondary
+      start: 0
+      end: 27
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21
+  cmHasSuffix(haystack, "a"):
+    fixed: cmHasSuffix(haystack, 'a')
+    labels:
+    - source: '"a"'
+      style: primary
+      start: 22
+      end: 25
+    - source: cmHasSuffix(haystack, "a")
+      style: secondary
+      start: 0
+      end: 26
+    - source: ','
+      style: secondary
+      start: 20
+      end: 21

+ 8 - 0
Utilities/ast-grep/rule-tests/cmhasliteralprefix-char.yml

@@ -0,0 +1,8 @@
+---
+id: cmhasliteralprefix-char
+valid:
+  - 'cmHasLiteralPrefix(haystack, "foo")'
+invalid:
+  - 'cmHasLiteralPrefix(haystack, "a")'
+  - 'cmHasLiteralPrefix(haystack, "\n")'
+  - "cmHasLiteralPrefix(haystack, \"'\")"

+ 8 - 0
Utilities/ast-grep/rule-tests/cmhasliteralsuffix-char.yml

@@ -0,0 +1,8 @@
+---
+id: cmhasliteralsuffix-char
+valid:
+  - 'cmHasLiteralSuffix(haystack, "foo")'
+invalid:
+  - 'cmHasLiteralSuffix(haystack, "a")'
+  - 'cmHasLiteralSuffix(haystack, "\n")'
+  - "cmHasLiteralSuffix(haystack, \"'\")"

+ 9 - 0
Utilities/ast-grep/rule-tests/cmhasprefix-char.yml

@@ -0,0 +1,9 @@
+---
+id: cmhasprefix-char
+valid:
+  - "cmHasPrefix(haystack, 'a')"
+  - 'cmHasPrefix(haystack, "foo")'
+invalid:
+  - 'cmHasPrefix(haystack, "a")'
+  - 'cmHasPrefix(haystack, "\n")'
+  - "cmHasPrefix(haystack, \"'\")"

+ 9 - 0
Utilities/ast-grep/rule-tests/cmhassuffix-char.yml

@@ -0,0 +1,9 @@
+---
+id: cmhassuffix-char
+valid:
+  - "cmHasSuffix(haystack, 'a')"
+  - 'cmHasSuffix(haystack, "foo")'
+invalid:
+  - 'cmHasSuffix(haystack, "a")'
+  - 'cmHasSuffix(haystack, "\n")'
+  - "cmHasSuffix(haystack, \"'\")"

+ 27 - 0
Utilities/ast-grep/rules/cmhasliteralprefix-char.yml

@@ -0,0 +1,27 @@
+---
+id: cmhasliteralprefix-char
+language: Cpp
+severity: warning
+message: "`cmHasLiteralPrefix` with a one-char prefix search should use `cmHasPrefix`"
+rule:
+  pattern: cmHasLiteralPrefix($HAYSTACK, $NEEDLE)
+constraints:
+  NEEDLE:
+    regex: '^"(.|\\.)"$'
+transform:
+  NEEDLE_CHANGE_QUOTE:
+    replace:
+      source: $NEEDLE
+      replace: '(^"|"$)'
+      by: "'"
+  NEEDLE_ESCAPE_SINGLE_QUOTE:
+    replace:
+      source: $NEEDLE_CHANGE_QUOTE
+      replace: "'''"
+      by: "'\\''"
+  NEEDLE_OUT:
+    replace:
+      source: $NEEDLE_ESCAPE_SINGLE_QUOTE
+      replace: '\\"'
+      by: '"'
+fix: cmHasPrefix($HAYSTACK, $NEEDLE_OUT)

+ 27 - 0
Utilities/ast-grep/rules/cmhasliteralsuffix-char.yml

@@ -0,0 +1,27 @@
+---
+id: cmhasliteralsuffix-char
+language: Cpp
+severity: warning
+message: "`cmHasLiteralSuffix` with a one-char suffix search should use `cmHasSuffix`"
+rule:
+  pattern: cmHasLiteralSuffix($HAYSTACK, $NEEDLE)
+constraints:
+  NEEDLE:
+    regex: '^"(.|\\.)"$'
+transform:
+  NEEDLE_CHANGE_QUOTE:
+    replace:
+      source: $NEEDLE
+      replace: '(^"|"$)'
+      by: "'"
+  NEEDLE_ESCAPE_SINGLE_QUOTE:
+    replace:
+      source: $NEEDLE_CHANGE_QUOTE
+      replace: "'''"
+      by: "'\\''"
+  NEEDLE_OUT:
+    replace:
+      source: $NEEDLE_ESCAPE_SINGLE_QUOTE
+      replace: '\\"'
+      by: '"'
+fix: cmHasSuffix($HAYSTACK, $NEEDLE_OUT)

+ 34 - 0
Utilities/ast-grep/rules/cmhasprefix-char.yml

@@ -0,0 +1,34 @@
+---
+id: cmhasprefix-char
+language: Cpp
+severity: warning
+message: "`cmHasPrefix` with a one-char prefix search should use `cmHasPrefix`"
+rule:
+  kind: string_literal
+  pattern: $ARG
+  follows:
+    regex: '(,|[(])'
+  inside:
+    matches: cmhasprefix-call
+    stopBy:
+      kind: call_expression
+constraints:
+  ARG:
+    regex: '^"(.|\\.)"$'
+transform:
+  ARG_CHANGE_QUOTE:
+    replace:
+      source: $ARG
+      replace: '(^"|"$)'
+      by: "'"
+  ARG_ESCAPE_SINGLE_QUOTE:
+    replace:
+      source: $ARG_CHANGE_QUOTE
+      replace: "'''"
+      by: "'\\''"
+  ARG_OUT:
+    replace:
+      source: $ARG_ESCAPE_SINGLE_QUOTE
+      replace: '\\"'
+      by: '"'
+fix: $ARG_OUT

+ 34 - 0
Utilities/ast-grep/rules/cmhassuffix-char.yml

@@ -0,0 +1,34 @@
+---
+id: cmhassuffix-char
+language: Cpp
+severity: warning
+message: "`cmHasSuffix` with a one-char suffix search should use `cmHasSuffix`"
+rule:
+  kind: string_literal
+  pattern: $ARG
+  follows:
+    regex: '(,|[(])'
+  inside:
+    matches: cmhassuffix-call
+    stopBy:
+      kind: call_expression
+constraints:
+  ARG:
+    regex: '^"(.|\\.)"$'
+transform:
+  ARG_CHANGE_QUOTE:
+    replace:
+      source: $ARG
+      replace: '(^"|"$)'
+      by: "'"
+  ARG_ESCAPE_SINGLE_QUOTE:
+    replace:
+      source: $ARG_CHANGE_QUOTE
+      replace: "'''"
+      by: "'\\''"
+  ARG_OUT:
+    replace:
+      source: $ARG_ESCAPE_SINGLE_QUOTE
+      replace: '\\"'
+      by: '"'
+fix: $ARG_OUT

+ 5 - 0
Utilities/ast-grep/utils/cmhasprefix-call.yml

@@ -0,0 +1,5 @@
+---
+id: cmhasprefix-call
+language: Cpp
+rule:
+  pattern: cmHasPrefix($$$)

+ 5 - 0
Utilities/ast-grep/utils/cmhassuffix-call.yml

@@ -0,0 +1,5 @@
+---
+id: cmhassuffix-call
+language: Cpp
+rule:
+  pattern: cmHasSuffix($$$)