Tests: don't use libc++ std::regex as a reference where it is wrong
libc++'s std::regex ignores match_prev_avail (and match_not_bol with it) for '^', matching it wherever a search resumes, and misses '\b' at the end of the subject. Search_NextPrevVisitReferenceMatches now builds its reference with std::cregex_iterator, which gets '^' right, and the '\b' replace case runs only against libstdc++. A new test checks that kte::Regex matches '^' only at the start of the subject; it is skipped for std::regex builds on libc++, which have the bug. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1949,16 +1949,14 @@ TEST(Search_NextPrevVisitReferenceMatches)
|
|||||||
for (std::size_t p = line.find(q); p != std::string::npos; p = line.find(q, p + q.size()))
|
for (std::size_t p = line.find(q); p != std::string::npos; p = line.find(q, p + q.size()))
|
||||||
want.emplace_back(y, p);
|
want.emplace_back(y, p);
|
||||||
} else {
|
} else {
|
||||||
std::size_t from = 0;
|
// regex_iterator rather than regex_search with
|
||||||
std::cmatch m;
|
// match_prev_avail, which libc++ ignores for '^'.
|
||||||
while (from <= line.size() &&
|
// For these patterns an empty match is never
|
||||||
std::regex_search(line.data() + from, line.data() + line.size(), m, rx,
|
// followed by a non-empty one at the same place,
|
||||||
from ? std::regex_constants::match_prev_avail
|
// so its stepping matches the rule above.
|
||||||
: std::regex_constants::match_default)) {
|
const char *lb = line.data();
|
||||||
const std::size_t pos = from + static_cast<std::size_t>(m.position(0));
|
for (std::cregex_iterator it(lb, lb + line.size(), rx), end; it != end; ++it)
|
||||||
want.emplace_back(y, pos);
|
want.emplace_back(y, static_cast<std::size_t>(it->position(0)));
|
||||||
from = pos + std::max<std::size_t>(static_cast<std::size_t>(m.length(0)), 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (nl == std::string::npos)
|
if (nl == std::string::npos)
|
||||||
break;
|
break;
|
||||||
@@ -2196,9 +2194,11 @@ TEST(RegexEngine_ReplaceMatchesStdRegex)
|
|||||||
const char *subjects[] = {"", "baaac", "one two three", "a-b-c", "xyz", "aaa", " lead", "tail "};
|
const char *subjects[] = {"", "baaac", "one two three", "a-b-c", "xyz", "aaa", " lead", "tail "};
|
||||||
const std::pair<const char *, const char *> cases[] = {
|
const std::pair<const char *, const char *> cases[] = {
|
||||||
{"a*", "X"}, {"a", "[$&]"}, {"(\\w+)", "<$1>"}, {"(o)(n)?", "$2$1"}, {"-", "$`|$'"},
|
{"a*", "X"}, {"a", "[$&]"}, {"(\\w+)", "<$1>"}, {"(o)(n)?", "$2$1"}, {"-", "$`|$'"},
|
||||||
{"\\s+", "$$"}, {"^", ">"}, {"$", "<"}, {"b|", "_"}, {"\\b", "|"},
|
{"\\s+", "$$"}, {"^", ">"}, {"$", "<"}, {"b|", "_"},
|
||||||
{"([a-z])([a-z])", "$2$1"}, {"(a)(x)?", "[$2]"},
|
{"([a-z])([a-z])", "$2$1"}, {"(a)(x)?", "[$2]"},
|
||||||
#if defined(__GLIBCXX__)
|
#if defined(__GLIBCXX__)
|
||||||
|
// libc++'s std::regex misses the \b at the end of the subject.
|
||||||
|
{"\\b", "|"},
|
||||||
// Read as libstdc++ reads them (libc++ differs): $0 is the match,
|
// Read as libstdc++ reads them (libc++ differs): $0 is the match,
|
||||||
// two digits are read greedily, missing groups expand to nothing.
|
// two digits are read greedily, missing groups expand to nothing.
|
||||||
{"b", "[$0]"}, {"(b?)", "<$10>"}, {"(a)", "[$2]"}, {"(a)(b)?", "$12"},
|
{"b", "[$0]"}, {"(b?)", "<$10>"}, {"(a)", "[$2]"}, {"(a)(b)?", "$12"},
|
||||||
@@ -2220,6 +2220,24 @@ TEST(RegexEngine_ReplaceMatchesStdRegex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// '^' matches only at the start of the subject, also when searching from
|
||||||
|
// inside it. Not with std::regex under libc++, which ignores
|
||||||
|
// match_prev_avail (and match_not_bol with it) for '^'.
|
||||||
|
#if defined(KTE_USE_PCRE2) || defined(__GLIBCXX__)
|
||||||
|
TEST(RegexEngine_CaretOnlyAtSubjectStart)
|
||||||
|
{
|
||||||
|
kte::Regex rx;
|
||||||
|
std::string err;
|
||||||
|
ASSERT_TRUE(rx.Compile("^", err));
|
||||||
|
std::size_t pos = 99, len = 99;
|
||||||
|
ASSERT_TRUE(rx.Search("ab", 0, pos, len));
|
||||||
|
ASSERT_EQ(pos, (std::size_t) 0);
|
||||||
|
ASSERT_TRUE(!rx.Search("ab", 1, pos, len));
|
||||||
|
ASSERT_TRUE(!rx.Search("ab", 2, pos, len));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// With PCRE2, catastrophic backtracking stops at the match limit and says
|
// With PCRE2, catastrophic backtracking stops at the match limit and says
|
||||||
// so, instead of hanging the editor.
|
// so, instead of hanging the editor.
|
||||||
TEST(RegexEngine_CatastrophicPatternStops)
|
TEST(RegexEngine_CatastrophicPatternStops)
|
||||||
|
|||||||
Reference in New Issue
Block a user