diff --git a/std/functional.d b/std/functional.d index 2c4104c14..f35d6ffdf 100644 --- a/std/functional.d +++ b/std/functional.d @@ -15,7 +15,7 @@ $(TR $(TH Function Name) $(TH Description) $(TD Joins a couple of functions into one that executes the original functions independently and returns a tuple with all the results. )) - $(TR $(TD $(LREF compose)), $(LREF pipe) + $(TR $(TD $(LREF compose), $(LREF pipe)) $(TD Join a couple of functions into one that executes the original functions one after the other, using one function's result for the next function's argument. @@ -23,7 +23,7 @@ $(TR $(TH Function Name) $(TH Description) $(TR $(TD $(LREF forward)) $(TD Forwards function arguments while saving ref-ness. )) - $(TR $(TD $(LREF lessThan)), $(LREF greaterThan)), $(D $(LREF equalTo) + $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo)) $(TD Ready-made predicate functions to compare two values. )) $(TR $(TD $(LREF memoize)) @@ -36,13 +36,13 @@ $(TR $(TH Function Name) $(TH Description) $(TD Creates a function that binds the first argument of a given function to a given value. )) - $(TR $(TD $(LREF reverseArgs)), $(LREF binaryReverseArgs) + $(TR $(TD $(LREF reverseArgs), $(LREF binaryReverseArgs)) $(TD Predicate that reverses the order of its arguments. )) $(TR $(TD $(LREF toDelegate)) $(TD Converts a callable to a delegate. )) - $(TR $(TD $(LREF unaryFun)), $(LREF binaryFun) + $(TR $(TD $(LREF unaryFun), $(LREF binaryFun)) $(TD Create a unary or binary function from a string. Most often used when defining algorithms on ranges. )) diff --git a/std/regex/internal/parser.d b/std/regex/internal/parser.d index 6498bddb7..e235292c6 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; @@ -1293,6 +1304,7 @@ if (isForwardRange!R && is(ElementType!R : dchar)) switch (op) { case Operator.Negate: + enforce(!stack.empty, "no operand for '^'"); stack.top = stack.top.inverted; break; case Operator.Union: @@ -1364,8 +1376,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 +1401,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 +1497,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 80e278bf6..1d4f9a26a 100644 --- a/std/regex/internal/tests.d +++ b/std/regex/internal/tests.d @@ -1082,3 +1082,27 @@ 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"); +} + +// bugzilla 17668 +@safe unittest +{ + import std.algorithm.searching; + auto e = collectException!RegexException(regex(q"<[^]>")); + assert(e.msg.canFind("no operand for '^'")); +} diff --git a/std/traits.d b/std/traits.d index ca3ffa365..ce47cc3cf 100644 --- a/std/traits.d +++ b/std/traits.d @@ -1262,20 +1262,32 @@ If a parameter doesn't have the default value, $(D void) is returned instead. template ParameterDefaults(func...) if (func.length == 1 && isCallable!func) { + alias param_names = ParameterIdentifierTuple!func; static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) { template Get(size_t i) { - // workaround scope escape check, see - // https://issues.dlang.org/show_bug.cgi?id=16582 - // should use return scope once available - enum get = (PT[i .. i+1] __args) @trusted - { - // If __args[0] is lazy, we force it to be evaluated like this. - PT[i] __pd_value = __args[0]; - PT[i]* __pd_val = &__pd_value; // workaround Bugzilla 16582 - return *__pd_val; - }; + // `PT[i .. i+1]` declares a parameter with an arbitrary name. + // To avoid a name clash, generate local names that are distinct + // from the parameter name, and mix them in. + enum name = param_names[i]; + enum args = "args" ~ (name == "args" ? "_" : ""); + enum val = "val" ~ (name == "val" ? "_" : ""); + enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); + mixin(" + // workaround scope escape check, see + // https://issues.dlang.org/show_bug.cgi?id=16582 + // should use return scope once available + enum get = (PT[i .. i+1] " ~ args ~ ") @trusted + { + // If the parameter is lazy, we force it to be evaluated + // like this. + auto " ~ val ~ " = " ~ args ~ "[0]; + auto " ~ ptr ~ " = &" ~ val ~ "; + // workaround Bugzilla 16582 + return *" ~ ptr ~ "; + }; + "); static if (is(typeof(get()))) enum Get = get(); else @@ -1313,6 +1325,17 @@ template ParameterDefaults(func...) static assert( ParameterDefaults!foo[3] == 0); } +@safe unittest // issue 17192 +{ + static void func(int i, int PT, int __pd_value, int __pd_val, int __args, + int name, int args, int val, int ptr, int args_, int val_, int ptr_) + { + } + alias Voids = ParameterDefaults!func; + static assert(Voids.length == 12); + foreach (V; Voids) static assert(is(V == void)); +} + /** * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility. */