This is a small but annoying problem: std.conv.text is defined like this:

string text(T...)(T args) { return textImpl!string(args); }

private S textImpl(S, U...)(U args)
{
    static if (U.length == 0)
    {
        return null;
    }
    else
    {
        auto result = to!S(args[0]);
        foreach (arg; args[1 .. $])
            result ~= to!S(arg);
        return result;
    }
}

If it is called with 0 arguments it will return null. This behaviour has caused several bugs in my code because combined with optional parens and UFCS, it is easy to accidentally call text with 0 args but have it look like passing a variable to a function. An example bug that I recently found in my code:

Token getNextToken(ref string input)
{
    assert(!input.empty);

    while (input.front.isSpace)
    {
        input.advance(1);
    }

    if (input.front == '$' || input.front.isAlpha)
    {
        if (input.front == '$')
        {
            //BUG
            text.advance(1);
        }
        auto identStr = input.getNextWord();
        if (keywords.canFind(identStr))
        {
return new Keyword(input.front == '$' ? '$' ~ identStr : identStr);
        }
        else
        {
            return new Identifier(identStr);
        }
    }
else if (input.front.isDigit || input.front == '-' || input.front == '+')
    {
        //etc.
    }
    else if (input.front == '"')
    {
        //etc.
    }
    //etc...
}

void advance(ref string input, size_t n)
{
    foreach (_; 0..n)
    {
        if (input.empty)
        {
            break;
        }
        input.popFront();
    }
}

The problem is that `advance` was not advancing the input, causing bugs in the lexing stage. Usually the compiler would warn me that no such variable `text` exists, but as I imported std.conv, `text` was in scope. Since `text` can take 0 or more arguments, and due to optional parens, `text.advance(1)` is a valid expression that does nothing. If std.conv.text took only 1 or more arguments and refused to compile with 0 arguments, then the compiler would signal an error as normal and I would not have to spend an hour hunting down this bug.

I would like to make a pull request to fix this, deprecating the 0-argument case and then eventually making it an error, but would this PR be accepted considering it technically breaks backwards-compatibility?

Reply via email to