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