diff --git a/std/regex/internal/parser.d b/std/regex/internal/parser.d index 6498bddb74a..0a1ecdec04b 100644 --- a/std/regex/internal/parser.d +++ b/std/regex/internal/parser.d @@ -1182,6 +1182,8 @@ if (isForwardRange!R && is(ElementType!R : dchar)) state = State.Start; break; default: + if (current >= privateUseStart && current <= privateUseEnd) + enforce(false, "no matching ']' found while parsing character class"); enforce(false, "invalid escape sequence"); } break; @@ -1256,8 +1258,17 @@ if (isForwardRange!R && is(ElementType!R : dchar)) end = parseUniHex(pat, 8); break; default: + if (current >= privateUseStart && current <= privateUseEnd) + enforce(false, "no matching ']' found while parsing character class"); error("invalid escape sequence"); } + // Lookahead to check if it's a \T + // where T is sub-pattern terminator in multi-pattern scheme + if (end == '\\' && !pat.empty) + { + if (pat.front >= privateUseStart && pat.front <= privateUseEnd) + enforce(false, "invalid escape sequence"); + } enforce(last <= end,"inverted range"); set.add(last, end + 1); state = State.Start; @@ -1364,8 +1375,7 @@ if (isForwardRange!R && is(ElementType!R : dchar)) "character class syntax error"); enforce(!opstack.empty, "unmatched ']'"); opstack.pop(); - next(); - if (opstack.empty) + if (!next() || opstack.empty) break L_CharsetLoop; auto pair = parseCharTerm(); if (!pair[0].empty)//not only operator e.g. -- or ~~ @@ -1390,10 +1400,13 @@ if (isForwardRange!R && is(ElementType!R : dchar)) } vstack.push(pair[0]); } - }while (!empty || !opstack.empty); while (!opstack.empty) - apply(opstack.pop(),vstack); + { + enforce(opstack.top != Operator.Open, + "no matching ']' found while parsing character class"); + apply(opstack.pop(), vstack); + } assert(vstack.length == 1); g.charsetToIr(vstack.top); } @@ -1483,6 +1496,13 @@ if (isForwardRange!R && is(ElementType!R : dchar)) g.markBackref(nref); break; default: + // Lookahead to check if it's a \T + // where T is sub-pattern terminator in multi-pattern scheme + if (current == '\\' && !pat.empty) + { + if (pat.front >= privateUseStart && current <= privateUseEnd) + enforce(false, "invalid escape sequence"); + } if (current >= privateUseStart && current <= privateUseEnd) { g.endPattern(current - privateUseStart + 1); diff --git a/std/regex/internal/tests.d b/std/regex/internal/tests.d index 80e278bf648..5415f45b4c3 100644 --- a/std/regex/internal/tests.d +++ b/std/regex/internal/tests.d @@ -1082,3 +1082,19 @@ alias Sequence(int B, int E) = staticIota!(B, E); assert(equal!equal(s.matchAll(ctr), outcomes)); assert(equal!equal(s.bmatch(r), outcomes)); } + +// bugzilla 17667 +@safe unittest +{ + import std.algorithm.searching : canFind; + void willThrow(T)(T arg, string msg) + { + auto e = collectException(regex(arg)); + assert(e.msg.canFind(msg), e.msg); + } + willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class"); + willThrow([r"[\", r"123"], "no matching ']' found while parsing character class"); + willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class"); + willThrow([r"[a-\", r"123"], "invalid escape sequence"); + willThrow([r"\", r"123"], "invalid escape sequence"); +}