Reduced code:
------------
import std.stdio;
struct MyFile
{
this(string filename) { }
auto byLine() { return 0; }
}
struct Wrapper
{
this(Args...)(Args args) {}
}
auto func(F = std.stdio.File)(bool[] args)
{
import std.algorithm.iteration : map;
foreach (x; args.map!(arg => arg ? Wrapper(
F("A").byLine) :
Wrapper(
0)))
{ }
}
void main()
{
import std.algorithm : each;
func!MyFile([ true, false ]);
func([ false ]);
}
------------
Compile with dmd -unittest -cov, and run the program. Here is the obtained .lst file:
------------
|import std.stdio;
|struct MyFile
|{
1| this(string filename) { }
1| auto byLine() { return 0; }
|}
|struct Wrapper
|{
3| this(Args...)(Args args) {}
|}
|auto func(F = std.stdio.File)(bool[] args)
|{
| import std.algorithm.iteration : map;
17| foreach (x; args.map!(arg => arg ? Wrapper(
0000000| F("A").byLine) :
2| Wrapper(
| 0)))
| { }
|}
|void main()
|{
| import std.algorithm : each;
1| func!MyFile([ true, false ]);
1| func([ false ]);
|}
util.d is 87% covered
------------
Note the line with 0000000. This is clearly wrong, because the first call to func!MyFile ought to have traversed both branches of the ?: operator, so F("A").byLine must have been invoked at least once.
Now comment out the second call to func (func([ false ])), recompile, and run again. Here is the obtained .lst file:
------------
|import std.stdio;
|struct MyFile
|{
1| this(string filename) { }
1| auto byLine() { return 0; }
|}
|struct Wrapper
|{
2| this(Args...)(Args args) {}
|}
|auto func(F = std.stdio.File)(bool[] args)
|{
| import std.algorithm.iteration : map;
11| foreach (x; args.map!(arg => arg ? Wrapper(
| F("A").byLine) :
1| Wrapper(
| 0)))
| { }
|}
|void main()
|{
| import std.algorithm : each;
1| func!MyFile([ true, false ]);
| //func([ false ]);
|}
util.d is 100% covered
------------
Note that the call to F("A").byLine is now *no longer part of the coverage count*. So here we have a paradoxical situation where decreasing code coverage (by not instantiating func with the default parameter for F) increases the reported code coverage to 100%.
Note that defaulting F to std.stdio.File seems to be an essential part of this bug; I could not get rid of the reference to std.stdio.File without making the bug also vanish. (I tried declaring my own version of File in a different module and importing that, but that made the bug disappear.)
Also, the odd formatting of the Wrapper lines are necessary to expose this bug; in the original, unreduced code, the lines were wrapped this way because the original code had longer lines that needed to be wrapped. However, wrapping lines should not affect the code coverage percentage(!).
Comment #1 by dlang-bugzilla — 2017-07-07T07:02:37Z
(In reply to hsteoh from comment #0)
> 0000000| F("A").byLine) :
FWIW, this line had no coverage counters at all before https://github.com/dlang/dmd/pull/5003 (so the bug as presented wasn't observable before that PR), but it seems unrelated and not the cause of a regression.
Comment #2 by robert.schadek — 2024-12-13T18:53:12Z