Browse Source

string: Allow references to unmatched groups in REGEX REPLACE

References to unmatched groups will be replaced with empty strings.

Issue: #26629
Fixes: #19012
Nikita Nemkin 11 months ago
parent
commit
ca65fa9a7f

+ 3 - 0
Help/command/string.rst

@@ -122,6 +122,9 @@ Search and Replace With Regular Expressions
   string instead of the beginning of each repeated search.
   See policy :policy:`CMP0186`.
 
+  The replacement expression may contain references to subexpressions that
+  didn't match anything. Previously, such references triggered an error.
+
 .. _`Regex Specification`:
 
 Regex Specification

+ 3 - 0
Help/release/dev/regex-fixes.rst

@@ -3,3 +3,6 @@ regex-fixes
 
 * Regular expressions match the ``^`` anchor at most once in repeated
   searches, at the start of the input.  See policy :policy:`CMP0186`.
+
+* References to unmatched groups are allowed, they are replaced with empty
+  strings.

+ 2 - 4
Source/cmStringReplaceHelper.cxx

@@ -61,10 +61,7 @@ bool cmStringReplaceHelper::Replace(std::string const& input,
       } else {
         // Replace with part of the match.
         auto n = replacement.Number;
-        auto start = this->RegularExpression.start(n);
-        if (start != std::string::npos) {
-          output += this->RegularExpression.match(n);
-        } else {
+        if (n > this->RegularExpression.num_groups()) {
           std::ostringstream error;
           error << "replace expression \"" << this->ReplaceExpression
                 << "\" contains an out-of-range escape for regex \""
@@ -72,6 +69,7 @@ bool cmStringReplaceHelper::Replace(std::string const& input,
           this->ErrorString = error.str();
           return false;
         }
+        output += this->RegularExpression.match(n);
       }
     }
 

+ 1 - 1
Tests/CMakeTests/StringTest.cmake.in

@@ -84,7 +84,7 @@ check_cmake_test(String
 # Execute each test listed in StringTestScript.cmake:
 #
 set(scriptname "@CMAKE_CURRENT_SOURCE_DIR@/StringTestScript.cmake")
-set(number_of_tests_expected 72)
+set(number_of_tests_expected 73)
 
 include("@CMAKE_CURRENT_SOURCE_DIR@/ExecuteScriptTests.cmake")
 execute_all_script_tests(${scriptname} number_of_tests_executed)

+ 4 - 0
Tests/CMakeTests/StringTestScript.cmake

@@ -116,6 +116,10 @@ elseif(testname STREQUAL regex_replace_index_too_small) # fail
 elseif(testname STREQUAL regex_replace_index_too_large) # fail
   string(REGEX REPLACE "^this (.*)$" "with \\1 \\2" v "this input")
 
+elseif(testname STREQUAL regex_replace_index_no_match) # pass
+  string(REGEX REPLACE "^(this (.*)|(that .*))$" "with \\1 \\2 \\3" v "this input")
+  message(STATUS "v='${v}'")
+
 elseif(testname STREQUAL compare_no_mode) # fail
   string(COMPARE)