diff --git a/stl/inc/regex b/stl/inc/regex index f5f8e75bf46..630349eaf89 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -1710,7 +1710,7 @@ private: bool _OctalDigits(); void _Do_ex_class(_Meta_type); bool _CharacterClassEscape(bool); - _Prs_ret _ClassEscape(bool); + _Prs_ret _ClassEscape2(); _Prs_ret _ClassAtom(); void _ClassRanges(); void _CharacterClass(); @@ -4017,22 +4017,29 @@ bool _Parser<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // return false; } + const bool _Negated = _Traits.isctype(_Char, _RxTraits::_Ch_upper); if (_Addit) { _Nfa._Add_class(); + // GH-992: Outside character class definitions, _Cls completely defines the character class + // so negating _Cls and negating the entire character class are equivalent. + // Since the former negation is defective, do the latter instead. + if (_Negated) { + _Nfa._Negate(); + } } - _Nfa._Add_named_class(_Cls, _Traits.isctype(_Char, _RxTraits::_Ch_upper)); + _Nfa._Add_named_class(_Cls, _Negated && !_Addit); _Next(); return true; } template -_Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassEscape(bool _Addit) { // check for class escape +_Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassEscape2() { // check for class escape if ((_L_flags & _L_esc_bsl) && _Char == _Esc_bsl) { // handle escape backslash if allowed _Val = _Esc_bsl; _Next(); return _Prs_chr; - } else if ((_L_flags & _L_esc_wsd) && _CharacterClassEscape(_Addit)) { + } else if ((_L_flags & _L_esc_wsd) && _CharacterClassEscape(false)) { return _Prs_set; } else if (_DecimalDigits(regex_constants::error_escape)) { // check for invalid value if (_Val != 0) { @@ -4049,7 +4056,7 @@ _Prs_ret _Parser<_FwdIt, _Elem, _RxTraits>::_ClassAtom() { // check for class at if (_Mchar == _Meta_esc) { // check for valid escape sequence _Next(); if (_L_flags & _L_grp_esc) { - return _ClassEscape(false); + return _ClassEscape2(); } else if ((_L_flags & _L_esc_ffn && _Do_ffn(_Char)) || (_L_flags & _L_esc_ffnx && _Do_ffnx(_Char))) { // advance to next character _Next(); diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index fcd27f0beba..a36ee7fdb09 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -629,6 +629,16 @@ void test_gh_5058() { } } +void test_gh_5160() { + // GH-5160 fixed mishandled negated character class escapes + // outside character class definitions + const test_wregex neg_regex(&g_regexTester, LR"(Y\S*Z)"); + neg_regex.should_search_match(L"xxxYxx\x0078xxxZxxx", L"Yxx\x0078xxxZ"); // U+0078 LATIN SMALL LETTER X + neg_regex.should_search_match(L"xxxYxx\x03C7xxxZxxx", L"Yxx\x03C7xxxZ"); // U+03C7 GREEK SMALL LETTER CHI + neg_regex.should_search_fail(L"xxxYxx xxxZxxx"); + neg_regex.should_search_fail(L"xxxYxx\x2009xxxZxxx"); // U+2009 THIN SPACE +} + int main() { test_dev10_449367_case_insensitivity_should_work(); test_dev11_462743_regex_collate_should_not_disable_regex_icase(); @@ -657,6 +667,7 @@ int main() { test_VSO_226914_word_boundaries(); test_gh_993(); test_gh_5058(); + test_gh_5160(); return g_regexTester.result(); }