Fix #10680 - Instead of passing receiver into the conversion function, (#10684)

just return the value and assign it to the receiver. Renamed the
conversion function and also cleaned up all the `typeof` calls, which
were very verbose.
This commit is contained in:
Steven Schveighoffer 2025-03-17 18:38:36 -04:00 committed by GitHub
parent cafe864533
commit 79cbde1ab6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -610,14 +610,14 @@ private template optionValidator(A...)
alias optionValidator = message; alias optionValidator = message;
} }
private void handleConversion(R)(string option, string value, R* receiver, private auto getoptTo(R)(string option, string value,
size_t idx, string file = __FILE__, size_t line = __LINE__) size_t idx, string file = __FILE__, size_t line = __LINE__)
{ {
import std.conv : to, ConvException; import std.conv : to, ConvException;
import std.format : format; import std.format : format;
try try
{ {
*receiver = to!(typeof(*receiver))(value); return to!R(value);
} }
catch (ConvException e) catch (ConvException e)
{ {
@ -876,12 +876,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
// (and potentially args[i + 1] too, but that comes later) // (and potentially args[i + 1] too, but that comes later)
args = args[0 .. i] ~ args[i + 1 .. $]; args = args[0 .. i] ~ args[i + 1 .. $];
static if (is(typeof(*receiver) == bool)) static if (is(typeof(*receiver)))
alias Target = typeof(*receiver);
else
// delegate
alias Target = void;
static if (is(Target == bool))
{ {
if (val.length) if (val.length)
{ {
// parse '--b=true/false' // parse '--b=true/false'
handleConversion(option, val, receiver, i); *receiver = getoptTo!(Target)(option, val, i);
} }
else else
{ {
@ -894,23 +900,23 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
import std.exception : enforce; import std.exception : enforce;
// non-boolean option, which might include an argument // non-boolean option, which might include an argument
enum isCallbackWithLessThanTwoParameters = enum isCallbackWithLessThanTwoParameters =
(is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) && (is(R == delegate) || is(Target == function)) &&
!is(typeof(receiver("", ""))); !is(typeof(receiver("", "")));
if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental) if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental)
{ {
// Eat the next argument too. Check to make sure there's one // Eat the next argument too. Check to make sure there's one
// to be eaten first, though. // to be eaten first, though.
enforce!GetOptException(i < args.length, enforce!GetOptException(i < args.length,
"Missing value for argument " ~ a ~ "."); "Missing value for argument " ~ a ~ ".");
val = args[i]; val = args[i];
args = args[0 .. i] ~ args[i + 1 .. $]; args = args[0 .. i] ~ args[i + 1 .. $];
} }
static if (is(typeof(*receiver) == enum) || static if (is(Target == enum) ||
is(typeof(*receiver) == string)) is(Target == string))
{ {
handleConversion(option, val, receiver, i); *receiver = getoptTo!Target(option, val, i);
} }
else static if (is(typeof(*receiver) : real)) else static if (is(Target : real))
{ {
// numeric receiver // numeric receiver
if (incremental) if (incremental)
@ -919,16 +925,16 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
} }
else else
{ {
handleConversion(option, val, receiver, i); *receiver = getoptTo!Target(option, val, i);
} }
} }
else static if (is(typeof(*receiver) == string)) else static if (is(Target == string))
{ {
// string receiver // string receiver
*receiver = to!(typeof(*receiver))(val); *receiver = getoptTo!(Target)(option, val, i);
} }
else static if (is(typeof(receiver) == delegate) || else static if (is(R == delegate) ||
is(typeof(*receiver) == function)) is(Target == function))
{ {
static if (is(typeof(receiver("", "")) : void)) static if (is(typeof(receiver("", "")) : void))
{ {
@ -952,29 +958,25 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
receiver(); receiver();
} }
} }
else static if (isArray!(typeof(*receiver))) else static if (isArray!(Target))
{ {
// array receiver // array receiver
import std.range : ElementEncodingType; import std.range : ElementEncodingType;
alias E = ElementEncodingType!(typeof(*receiver)); alias E = ElementEncodingType!(Target);
if (arraySep == "") if (arraySep == "")
{ {
E tmp; *receiver ~= getoptTo!E(option, val, i);
handleConversion(option, val, &tmp, i);
*receiver ~= tmp;
} }
else else
{ {
foreach (elem; val.splitter(arraySep)) foreach (elem; val.splitter(arraySep))
{ {
E tmp; *receiver ~= getoptTo!E(option, elem, i);
handleConversion(option, elem, &tmp, i);
*receiver ~= tmp;
} }
} }
} }
else static if (isAssociativeArray!(typeof(*receiver))) else static if (isAssociativeArray!(Target))
{ {
// hash receiver // hash receiver
alias K = typeof(receiver.keys[0]); alias K = typeof(receiver.keys[0]);
@ -991,14 +993,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'."); ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
auto key = input[0 .. j]; auto key = input[0 .. j];
auto value = input[j + 1 .. $]; auto value = input[j + 1 .. $];
return tuple(getoptTo!K("", key, 0), getoptTo!V("", value, 0));
K k;
handleConversion("", key, &k, 0);
V v;
handleConversion("", value, &v, 0);
return tuple(k,v);
} }
static void setHash(Range)(R receiver, Range range) static void setHash(Range)(R receiver, Range range)
@ -1013,7 +1008,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
setHash(receiver, val.splitter(arraySep)); setHash(receiver, val.splitter(arraySep));
} }
else else
static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof); static assert(false, "getopt does not know how to handle the type " ~ R.stringof);
} }
} }
@ -1099,6 +1094,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
} }
// https://github.com/dlang/phobos/issues/10680
@safe unittest
{
arraySep = ",";
scope(exit) arraySep = "";
const(string)[] s;
string[] args = ["program.name", "-s", "a", "-s", "b", "-s", "c,d,e"];
getopt(args, "values|s", &s);
assert(s == ["a", "b", "c", "d", "e"]);
}
/** /**
The option character (default '-'). The option character (default '-').