Bug 4927 – writefln silently ignores arguments not present in the format string
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
x86
OS
Windows
Creation time
2010-09-23T15:33:40Z
Last change time
2021-01-09T22:30:03Z
Keywords
bootcamp, spec
Assigned to
Andrei Alexandrescu
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2010-09-23T15:33:40Z
With dmd 2.049 this program:
import std.stdio: writefln;
void main() {
writefln("%d", 1, 2);
}
Prints:
1
But in this case I expect a compile-time error (or equivalent run-time error if the compiler is lazy and doesn't perform such very basic tests at compile-time), because the format string doesn't contain the same things as the given arguments.
In this case printing 12 is not good, because when a writefln is used, it's better to avoid possible bugs to enforce that arguments and format string match exactly.
See also bug 4458
Comment #1 by k.hara.pg — 2011-09-10T08:35:19Z
That is a design of formattedWrite. Andrei talked:
https://github.com/D-Programming-Language/phobos/pull/231#issuecomment-1995490
> Phobos used to make a fuss about ignored arguments, but I changed that. This is
> because it's often good to present several arguments and let the user choose the
> formatting string (which may ignore some argument). This is most obvious with
> positional parameters. The std.log implementation needs something like that.
Comment #2 by andrei — 2011-09-10T09:28:47Z
One possibility would be to silently ignore arguments is positional parameters are used, and not ignore them otherwise.
Comment #3 by bearophile_hugs — 2011-09-10T10:50:28Z
If a programmer wants to format just the first argument, then this is the code to write:
writef("%d", 1);
writefln(2);
Introducing/keeping a muddy semantics to avoid writing one more function call like is not a so good design.
Keeping designs semantically clean is usually better, because it helps avoid bugs and more importantly it avoids unexpected corner cases and unwanted side interactions.
The introduction of a special case with muddy semantics is sometimes acceptable, when it gives _significant_ advantages, that means it makes something else significantly more handy or significantly simpler, etc. But in this case I think it makes code only a bit more handy, while increasing the probability of bugs.
----------------
(In reply to comment #2)
> One possibility would be to silently ignore arguments is positional parameters
> are used, and not ignore them otherwise.
This is better than the current situation.
> it's often good to present several arguments and let the user choose the
> formatting string (which may ignore some argument).
Usually explicit is better than implicit. If this is the desired behaviour, then the right thing is to use/introduce an explicit syntax to do it. Positional arguments are meant to do this, I presume.
Comment #4 by andrej.mitrovich — 2013-06-29T20:05:49Z
*** Issue 10489 has been marked as a duplicate of this issue. ***
Comment #5 by bearophile_hugs — 2013-06-29T20:33:16Z
(In reply to comment #4)
> *** Issue 10489 has been marked as a duplicate of this issue. ***
The body of Issue 10489:
In DMD 2.064alpha this program compiles and runs with no errors, warnings or
run-time exceptions:
import std.stdio;
void main() {
writefln("%d %d", 1, 2, 3);
}
While this gives a run-time exception:
import std.string;
void main() {
format("%d %d", 1, 2, 3);
}
std.format.FormatException@...\dmd2\src\phobos\std\string.d(2346): Orphan
format arguments: args[2..3]
To catch some programmer mistakes I suggest to turn this into an
exception/error:
writefln("%d %d", 1, 2, 3);
But as stated by Andrei Alexandrescu:
http://forum.dlang.org/post/[email protected]
> The only point I'd negotiate would be to not throw with positional
> arguments, and throw with sequential arguments. All code that cares uses
> positional specifiers anyway.
So according to Andrei this should be accepted:
import std.stdio;
void main() {
writefln("%2$s %1$s", 1, 2, 3);
}
Currently this is accepted (and it prints "A B10"), but I think it should be
not accepted:
import std.stdio;
void main() {
writefln("A%2$s B%1$s", 10);
}
Comment #6 by smjg — 2013-12-30T11:57:11Z
This is actually per spec:
http://dlang.org/phobos/std_format.html
"If there are more remaining arguments than needed by the format specification, they are ignored but only if at least one argument was formatted."
But I agree that it is a bad design, and that the means of ignoring arguments should be explicit.
(In reply to comment #2)
> One possibility would be to silently ignore arguments is positional parameters
> are used, and not ignore them otherwise.
I'm not sure. If you're overriding the normal sequence by using positional arguments, are you showing that you want any unreferenced arguments to be ignored? Or is it more likely that you have inadvertently left an argument out of the format string? Moreover, what if you mix positional and non-positional format specifiers in the same format string?
Maybe what we should do is define %z or something to mean "format as empty string". It would typically be used in two ways:
- when using non-positional format specifiers, to skip an argument
- when using positional format specifiers, as a dummy reference to an otherwise unreferenced argument.
(In reply to comment #5)
> In DMD 2.064alpha this program compiles and runs with no errors, warnings or
> run-time exceptions:
>
> import std.stdio;
> void main() {
> writefln("%d %d", 1, 2, 3);
> }
>
> While this gives a run-time exception:
>
> import std.string;
> void main() {
> format("%d %d", 1, 2, 3);
> }
See issue 4532.
> Currently this is accepted (and it prints "A B10"), but I think it should be
> not accepted:
>
> import std.stdio;
> void main() {
> writefln("A%2$s B%1$s", 10);
> }
I agree, this should error.
Comment #7 by pro.mathias.lang — 2021-01-09T22:30:03Z
As mentioned, the issue in the original message was per spec.
However, the part with positional argument was not, and has since been fixed:
```D
import std.stdio;
void main() {
writefln("A%2$s B%1$s", 10);
}
```
This correctly errors out now.