Skip to content

Commit d4f9768

Browse files
committed
[libc++] <regex>: Make unmatched backrefs should always succeed in ECMAScript mode.
Fix #154408
1 parent 961b052 commit d4f9768

File tree

2 files changed

+58
-13
lines changed

2 files changed

+58
-13
lines changed

libcxx/include/regex

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,7 +1668,7 @@ void __end_marked_subexpression<_CharT>::__exec(__state& __s) const {
16681668

16691669
// __back_ref
16701670

1671-
template <class _CharT>
1671+
template <class _CharT, bool _UnmatchedAlwaysSucceed>
16721672
class __back_ref : public __owns_one_state<_CharT> {
16731673
typedef __owns_one_state<_CharT> base;
16741674

@@ -1682,8 +1682,8 @@ public:
16821682
_LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void __exec(__state&) const;
16831683
};
16841684

1685-
template <class _CharT>
1686-
void __back_ref<_CharT>::__exec(__state& __s) const {
1685+
template <class _CharT, bool _UnmatchedAlwaysSucceed>
1686+
void __back_ref<_CharT, _UnmatchedAlwaysSucceed>::__exec(__state& __s) const {
16871687
if (__mexp_ > __s.__sub_matches_.size())
16881688
std::__throw_regex_error<regex_constants::error_backref>();
16891689
sub_match<const _CharT*>& __sm = __s.__sub_matches_[__mexp_ - 1];
@@ -1697,6 +1697,9 @@ void __back_ref<_CharT>::__exec(__state& __s) const {
16971697
__s.__do_ = __state::__reject;
16981698
__s.__node_ = nullptr;
16991699
}
1700+
} else if constexpr (_UnmatchedAlwaysSucceed) {
1701+
__s.__do_ = __state::__accept_but_not_consume;
1702+
__s.__node_ = this->first();;
17001703
} else {
17011704
__s.__do_ = __state::__reject;
17021705
__s.__node_ = nullptr;
@@ -1705,7 +1708,7 @@ void __back_ref<_CharT>::__exec(__state& __s) const {
17051708

17061709
// __back_ref_icase
17071710

1708-
template <class _CharT, class _Traits>
1711+
template <class _CharT, class _Traits, bool _UnmatchedAlwaysSucceed>
17091712
class __back_ref_icase : public __owns_one_state<_CharT> {
17101713
typedef __owns_one_state<_CharT> base;
17111714

@@ -1721,8 +1724,8 @@ public:
17211724
_LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void __exec(__state&) const;
17221725
};
17231726

1724-
template <class _CharT, class _Traits>
1725-
void __back_ref_icase<_CharT, _Traits>::__exec(__state& __s) const {
1727+
template <class _CharT, class _Traits, bool _UnmatchedAlwaysSucceed>
1728+
void __back_ref_icase<_CharT, _Traits, _UnmatchedAlwaysSucceed>::__exec(__state& __s) const {
17261729
sub_match<const _CharT*>& __sm = __s.__sub_matches_[__mexp_ - 1];
17271730
if (__sm.matched) {
17281731
ptrdiff_t __len = __sm.second - __sm.first;
@@ -1739,6 +1742,11 @@ void __back_ref_icase<_CharT, _Traits>::__exec(__state& __s) const {
17391742
__s.__node_ = nullptr;
17401743
}
17411744
} else {
1745+
if constexpr (_UnmatchedAlwaysSucceed) {
1746+
__s.__do_ = __state::__accept_but_not_consume;
1747+
__s.__node_ = this->first();
1748+
return;
1749+
}
17421750
__not_equal:
17431751
__s.__do_ = __state::__reject;
17441752
__s.__node_ = nullptr;
@@ -1747,7 +1755,7 @@ void __back_ref_icase<_CharT, _Traits>::__exec(__state& __s) const {
17471755

17481756
// __back_ref_collate
17491757

1750-
template <class _CharT, class _Traits>
1758+
template <class _CharT, class _Traits, bool _UnmatchedAlwaysSucceed>
17511759
class __back_ref_collate : public __owns_one_state<_CharT> {
17521760
typedef __owns_one_state<_CharT> base;
17531761

@@ -1763,8 +1771,8 @@ public:
17631771
_LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void __exec(__state&) const;
17641772
};
17651773

1766-
template <class _CharT, class _Traits>
1767-
void __back_ref_collate<_CharT, _Traits>::__exec(__state& __s) const {
1774+
template <class _CharT, class _Traits, bool _UnmatchedAlwaysSucceed>
1775+
void __back_ref_collate<_CharT, _Traits, _UnmatchedAlwaysSucceed>::__exec(__state& __s) const {
17681776
sub_match<const _CharT*>& __sm = __s.__sub_matches_[__mexp_ - 1];
17691777
if (__sm.matched) {
17701778
ptrdiff_t __len = __sm.second - __sm.first;
@@ -1781,6 +1789,11 @@ void __back_ref_collate<_CharT, _Traits>::__exec(__state& __s) const {
17811789
__s.__node_ = nullptr;
17821790
}
17831791
} else {
1792+
if constexpr (_UnmatchedAlwaysSucceed) {
1793+
__s.__do_ = __state::__accept_but_not_consume;
1794+
__s.__node_ = this->first();
1795+
return;
1796+
}
17841797
__not_equal:
17851798
__s.__do_ = __state::__reject;
17861799
__s.__node_ = nullptr;
@@ -2565,6 +2578,7 @@ private:
25652578
bool __greedy = true);
25662579
__bracket_expression<_CharT, _Traits>* __start_matching_list(bool __negate);
25672580
void __push_char(value_type __c);
2581+
template <bool _UnmatchedAlwaysSucceed = false>
25682582
void __push_back_ref(int __i);
25692583
void __push_alternation(__owns_one_state<_CharT>* __sa, __owns_one_state<_CharT>* __sb);
25702584
void __push_begin_marked_subexpression();
@@ -3807,7 +3821,7 @@ basic_regex<_CharT, _Traits>::__parse_decimal_escape(_ForwardIterator __first, _
38073821
}
38083822
if (__v == 0 || __v > mark_count())
38093823
std::__throw_regex_error<regex_constants::error_backref>();
3810-
__push_back_ref(__v);
3824+
__push_back_ref<true>(__v);
38113825
}
38123826
}
38133827
return __first;
@@ -4149,13 +4163,14 @@ void basic_regex<_CharT, _Traits>::__push_word_boundary(bool __invert) {
41494163
}
41504164

41514165
template <class _CharT, class _Traits>
4166+
template <bool _UnmatchedAlwaysSucceed>
41524167
void basic_regex<_CharT, _Traits>::__push_back_ref(int __i) {
41534168
if (flags() & icase)
4154-
__end_->first() = new __back_ref_icase<_CharT, _Traits>(__traits_, __i, __end_->first());
4169+
__end_->first() = new __back_ref_icase<_CharT, _Traits, _UnmatchedAlwaysSucceed>(__traits_, __i, __end_->first());
41554170
else if (flags() & collate)
4156-
__end_->first() = new __back_ref_collate<_CharT, _Traits>(__traits_, __i, __end_->first());
4171+
__end_->first() = new __back_ref_collate<_CharT, _Traits, _UnmatchedAlwaysSucceed>(__traits_, __i, __end_->first());
41574172
else
4158-
__end_->first() = new __back_ref<_CharT>(__i, __end_->first());
4173+
__end_->first() = new __back_ref<_CharT, _UnmatchedAlwaysSucceed>(__i, __end_->first());
41594174
__end_ = static_cast<__owns_one_state<_CharT>*>(__end_->first());
41604175
}
41614176

libcxx/test/std/re/re.alg/re.alg.search/ecma.pass.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,21 @@ int main(int, char**)
762762
assert(m.position(0) == 0);
763763
assert(m.str(0) == s);
764764
}
765+
{
766+
std::cmatch m;
767+
const char s[] = "a";
768+
assert(std::regex_search(s, m, std::regex{"(a()|)\\2a"}));
769+
assert(m.size() == 3);
770+
assert(!m.prefix().matched);
771+
assert(m.prefix().first == s);
772+
assert(m.prefix().second == m[0].first);
773+
assert(!m.suffix().matched);
774+
assert(m.suffix().first == m[0].second);
775+
assert(m.suffix().second == s + std::char_traits<char>::length(s));
776+
assert(m.length(0) >= 0 && static_cast<std::size_t>(m.length(0)) == std::char_traits<char>::length(s));
777+
assert(m.position(0) == 0);
778+
assert(m.str(0) == s);
779+
}
765780

766781
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
767782
{
@@ -1503,6 +1518,21 @@ int main(int, char**)
15031518
assert(m.position(0) == 0);
15041519
assert(m.str(0) == s);
15051520
}
1521+
{
1522+
std::wcmatch m;
1523+
const wchar_t s[] = L"a";
1524+
assert(std::regex_search(s, m, std::wregex{L"(a()|)\\2a"}));
1525+
assert(m.size() == 3);
1526+
assert(!m.prefix().matched);
1527+
assert(m.prefix().first == s);
1528+
assert(m.prefix().second == m[0].first);
1529+
assert(!m.suffix().matched);
1530+
assert(m.suffix().first == m[0].second);
1531+
assert(m.suffix().second == s + std::char_traits<wchar_t>::length(s));
1532+
assert(m.length(0) >= 0 && static_cast<std::size_t>(m.length(0)) == std::char_traits<wchar_t>::length(s));
1533+
assert(m.position(0) == 0);
1534+
assert(m.str(0) == s);
1535+
}
15061536
#endif // TEST_HAS_NO_WIDE_CHARACTERS
15071537

15081538
return 0;

0 commit comments

Comments
 (0)