Fix issue #9829 std.conv.parse!(int, string) counts when doCount is false (#10674)

This commit is contained in:
Aditya Chincholkar 2025-03-17 19:45:40 +05:30 committed by GitHub
parent 271ae6219d
commit fd4979ab99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2470,7 +2470,8 @@ if (isIntegral!Target && !is(Target == enum) &&
alias source = s;
}
size_t count = 0;
static if (doCount)
size_t count = 0;
if (source.empty)
goto Lerr;
@ -2485,7 +2486,8 @@ if (isIntegral!Target && !is(Target == enum) &&
sign = true;
goto case '+';
case '+':
++count;
static if (doCount)
++count;
source.popFront();
if (source.empty)
@ -2504,7 +2506,8 @@ if (isIntegral!Target && !is(Target == enum) &&
{
Target v = cast(Target) c;
++count;
static if (doCount)
++count;
source.popFront();
while (!source.empty)
@ -2520,7 +2523,8 @@ if (isIntegral!Target && !is(Target == enum) &&
// Note: `v` can become negative here in case of parsing
// the most negative value:
v = cast(Target) (v * 10 + c);
++count;
static if (doCount)
++count;
source.popFront();
}
else
@ -2877,7 +2881,8 @@ do
alias s = source;
}
size_t count = 0;
static if (doCount)
size_t count = 0;
auto found = false;
do
{
@ -2904,7 +2909,8 @@ do
auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow);
enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion");
v = cast(Target) nextv;
++count;
static if (doCount)
++count;
s.popFront();
found = true;
} while (!s.empty);
@ -3163,36 +3169,35 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
enforce(!p.empty, bailOut());
size_t count = 0;
bool sign = false;
switch (p.front)
{
case '-':
sign = true;
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty, bailOut());
if (toLower(p.front) == 'i')
goto case 'i';
break;
case '+':
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty, bailOut());
break;
case 'i': case 'I':
// inf
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'F',
bailOut("error converting input to floating point"));
// skip past the last 'f'
++count;
static if (doCount) ++count;
p.popFront();
advanceSource();
static if (doCount)
@ -3210,7 +3215,7 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
bool startsWithZero = p.front == '0';
if (startsWithZero)
{
++count;
static if (doCount) ++count;
p.popFront();
if (p.empty)
{
@ -3228,23 +3233,23 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
isHex = p.front == 'x' || p.front == 'X';
if (isHex)
{
++count;
static if (doCount) ++count;
p.popFront();
}
}
else if (toLower(p.front) == 'n')
{
// nan
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'A',
bailOut("error converting input to floating point"));
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
// skip past the last 'n'
++count;
static if (doCount) ++count;
p.popFront();
advanceSource();
static if (doCount)
@ -3332,14 +3337,14 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
exp += expIter;
}
exp -= dot;
++count;
static if (doCount) ++count;
p.popFront();
if (p.empty)
break;
i = p.front;
if (i == '_')
{
++count;
static if (doCount) ++count;
p.popFront();
if (p.empty)
break;
@ -3348,7 +3353,7 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
}
if (i == '.' && !dot)
{
++count;
static if (doCount) ++count;
p.popFront();
dot += expIter;
}
@ -3373,14 +3378,14 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
char sexp = 0;
int e = 0;
++count;
static if (doCount) ++count;
p.popFront();
enforce(!p.empty, new ConvException("Unexpected end of input"));
switch (p.front)
{
case '-': sexp++;
goto case;
case '+': ++count;
case '+': static if (doCount) ++count;
p.popFront();
break;
default: {}
@ -3392,7 +3397,7 @@ if (isFloatingPoint!Target && !is(Target == enum) &&
{
e = e * 10 + p.front - '0';
}
++count;
static if (doCount) ++count;
p.popFront();
sawDigits = true;
}
@ -4098,7 +4103,12 @@ if (isDynamicArray!Target && !is(Target == enum) &&
auto result = appender!Target();
parseCheck!s(lbracket);
size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
static if (doCount)
size_t count = 1;
static if (doCount)
count += skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
@ -4106,36 +4116,54 @@ if (isDynamicArray!Target && !is(Target == enum) &&
s.popFront();
static if (doCount)
{
return tuple!("data", "count")(result.data, ++count);
++count;
return tuple!("data", "count")(result.data, count);
}
else
{
return result.data;
}
}
for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
for (;;)
{
if (!s.empty && s.front == rbracket)
break;
auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s);
result ~= r.data;
count += r.count + skipWS!(Source, Yes.doCount)(s);
static if (doCount)
{
auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s);
result ~= r.data;
count += r.count;
count += skipWS!(Source, Yes.doCount)(s);
}
else
{
auto r = parseElement!(WideElementType!Target, Source, No.doCount)(s);
result ~= r;
skipWS!(Source, No.doCount)(s);
}
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
break;
s.popFront();
static if (doCount)
++count;
static if (doCount)
count += skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
}
parseCheck!s(rbracket);
static if (doCount)
{
return tuple!("data", "count")(result.data, ++count);
++count;
return tuple!("data", "count")(result.data, count);
}
else
{
return result.data;
}
}
///
@safe pure unittest
{
@ -4286,7 +4314,12 @@ if (isStaticArray!Target && !is(Target == enum) &&
Target result = void;
parseCheck!s(lbracket);
size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
static if (doCount)
size_t count = 1;
static if (doCount)
count += skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
@ -4298,7 +4331,8 @@ if (isStaticArray!Target && !is(Target == enum) &&
s.popFront();
static if (doCount)
{
return tuple!("data", "count")(result, ++count);
++count;
return tuple!("data", "count")(result, count);
}
else
{
@ -4306,13 +4340,23 @@ if (isStaticArray!Target && !is(Target == enum) &&
}
}
}
for (size_t i = 0; ; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
for (size_t i = 0; ; )
{
if (i == result.length)
goto Lmanyerr;
auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s);
result[i++] = r.data;
count += r.count + skipWS!(Source, Yes.doCount)(s);
static if (doCount)
{
auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s);
result[i++] = r.data;
count += r.count;
count += skipWS!(Source, Yes.doCount)(s);
}
else
{
auto r = parseElement!(ElementType!Target, Source, No.doCount)(s);
result[i++] = r;
skipWS!(Source, No.doCount)(s);
}
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
@ -4321,21 +4365,27 @@ if (isStaticArray!Target && !is(Target == enum) &&
goto Lfewerr;
break;
}
s.popFront();
static if (doCount)
++count;
static if (doCount)
count += skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
}
parseCheck!s(rbracket);
static if (doCount)
{
return tuple!("data", "count")(result, ++count);
++count;
return tuple!("data", "count")(result, count);
}
else
{
return result;
}
Lmanyerr:
throw parseError(text("Too many elements in input, ", result.length, " elements expected."));
Lfewerr:
throw parseError(text("Too few elements in input, ", result.length, " elements expected."));
}
@ -4393,46 +4443,60 @@ if (isAssociativeArray!Target && !is(Target == enum) &&
alias ValType = typeof(Target.init.values[0]);
Target result;
static if (doCount) size_t count;
parseCheck!s(lbracket);
size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
static if (doCount)
count = 1 + skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
{
s.popFront();
static if (doCount)
return tuple!("data", "count")(result, count + 1);
else
return result;
}
for (;;)
{
static if (doCount)
{
return tuple!("data", "count")(result, ++count);
auto key = parseElement!(KeyType, Source, Yes.doCount)(s);
count += key.count + skipWS!(Source, Yes.doCount)(s);
parseCheck!s(keyval);
count += 1 + skipWS!(Source, Yes.doCount)(s);
auto val = parseElement!(ValType, Source, Yes.doCount)(s);
count += val.count + skipWS!(Source, Yes.doCount)(s);
result[key.data] = val.data;
}
else
{
return result;
auto key = parseElement!(KeyType, Source, No.doCount)(s);
skipWS!(Source, No.doCount)(s);
parseCheck!s(keyval);
skipWS!(Source, No.doCount)(s);
auto val = parseElement!(ValType, Source, No.doCount)(s);
skipWS!(Source, No.doCount)(s);
result[key] = val;
}
}
for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
{
auto key = parseElement!(KeyType, Source, Yes.doCount)(s);
count += key.count + skipWS!(Source, Yes.doCount)(s);
parseCheck!s(keyval);
count += 1 + skipWS!(Source, Yes.doCount)(s);
auto val = parseElement!(ValType, Source, Yes.doCount)(s);
count += val.count + skipWS!(Source, Yes.doCount)(s);
result[key.data] = val.data;
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
break;
s.popFront();
static if (doCount)
count += 1 + skipWS!(Source, Yes.doCount)(s);
else
skipWS!(Source, No.doCount)(s);
}
parseCheck!s(rbracket);
static if (doCount)
{
return tuple!("data", "count")(result, ++count);
}
return tuple!("data", "count")(result, count + 1);
else
{
return result;
}
}
///
@ -4489,7 +4553,9 @@ private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source
if (isInputRange!Source && isSomeChar!(ElementType!Source))
{
parseCheck!s('\\');
size_t count = 1;
size_t count;
static if (doCount)
count = 1;
if (s.empty)
throw parseError("Unterminated escape sequence");
@ -4507,15 +4573,15 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
throw parseError("Hex digit is missing");
return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0';
}
// We need to do octals separate, because they need a lookahead to find out,
// where the escape sequence ends.
auto first = s.front;
if (first >= '0' && first <= '7')
{
dchar c1 = s.front;
++count;
s.popFront();
static if (doCount)
++count;
if (s.empty)
{
static if (doCount)
@ -4539,8 +4605,9 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
return cast (dchar)(c1 - '0');
}
}
++count;
s.popFront();
static if (doCount)
++count;
dchar c3 = s.front;
if (c3 < '0' || c3 > '7')
{
@ -4553,8 +4620,9 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
return cast (dchar) (8 * (c1 - '0') + (c2 - '0'));
}
}
++count;
s.popFront();
static if (doCount)
++count;
if (c1 > '3')
throw parseError("Octal sequence is larger than \\377");
static if (doCount)
@ -4585,14 +4653,16 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
case 'x':
result = getHexDigit() << 4;
result |= getHexDigit();
count += 2;
static if (doCount)
count += 2;
break;
case 'u':
result = getHexDigit() << 12;
result |= getHexDigit() << 8;
result |= getHexDigit() << 4;
result |= getHexDigit();
count += 4;
static if (doCount)
count += 4;
break;
case 'U':
result = getHexDigit() << 28;
@ -4603,7 +4673,8 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
result |= getHexDigit() << 8;
result |= getHexDigit() << 4;
result |= getHexDigit();
count += 8;
static if (doCount)
count += 8;
break;
default:
throw parseError("Unknown escape character " ~ to!string(s.front));
@ -4643,7 +4714,6 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
// https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
//'\&amp;', '\&quot;',
];
foreach (i ; 0 .. s1.length)
{
assert(s2[i] == parseEscape(s1[i]));
@ -4696,7 +4766,9 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
}
parseCheck!s('\"');
size_t count = 1;
size_t count;
static if (doCount)
count = 1;
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == '\"')
@ -4704,13 +4776,13 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
s.popFront();
static if (doCount)
{
return tuple!("data", "count")(result.data, ++count);
count++;
return tuple!("data", "count")(result.data, count);
}
else
{
return result.data;
}
}
while (true)
{
@ -4722,20 +4794,30 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
s.popFront();
static if (doCount)
{
return tuple!("data", "count")(result.data, ++count);
count++;
return tuple!("data", "count")(result.data, count);
}
else
{
return result.data;
}
case '\\':
auto r = parseEscape!(typeof(s), Yes.doCount)(s);
result.put(r[0]);
count += r[1];
static if (doCount)
{
auto r = parseEscape!(typeof(s), Yes.doCount)(s);
result.put(r[0]);
count += r[1];
}
else
{
auto r = parseEscape!(typeof(s), No.doCount)(s);
result.put(r);
}
break;
default:
result.put(s.front);
++count;
static if (doCount)
count++;
s.popFront();
break;
}
@ -4751,21 +4833,30 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
Unqual!Target c;
parseCheck!s('\'');
size_t count = 1;
size_t count;
static if (doCount)
count = 1;
if (s.empty)
throw convError!(Source, Target)(s);
++count; // for the following if-else sequence
static if (doCount)
count++;
if (s.front != '\\')
{
c = s.front;
s.popFront();
}
else
c = parseEscape(s);
{
static if (doCount)
c = parseEscape!(typeof(s), Yes.doCount)(s).data;
else
c = parseEscape!(typeof(s), No.doCount)(s);
}
parseCheck!s('\'');
static if (doCount)
{
return tuple!("data", "count")(c, ++count);
count++;
return tuple!("data", "count")(c, count);
}
else
{