Open
Description
Code coverage reporting is incorrect at the call site when a macro contains an unused branch. Example:
// macro.c
#include <stdio.h>
int enabled = 0;
#define MY_ID(x) 7
#define MY_LOG(fmt, ...) \
{ \
if (enabled) { \
printf(fmt, ## __VA_ARGS__); \
} \
}
int main(int argc, char *argv[]) {
MY_LOG("%d, %s, %d\n",
MY_ID(argc),
"a",
1);
}
# build-c.sh
/usr/bin/clang --version | /bin/grep "clang version"
/usr/bin/clang -fprofile-instr-generate -fcoverage-mapping coverage.c -o coverage
./coverage
/usr/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
/usr/bin/llvm-cov show ./coverage -instr-profile=default.profdata
The coverage report confusingly marks the MY_LOG
usage in main
as partially covered:
$ ./build-c.sh
clang version 19.1.7
1| |#include <stdio.h>
2| |
3| |int enabled = 0;
4| |
5| 0|#define MY_ID(x) 7
6| |
7| |#define MY_LOG(fmt, ...) \
8| 1| { \
9| 1| if (enabled) { \
10| 0| printf(fmt, ## __VA_ARGS__); \
11| 0| } \
12| 1| }
13| |
14| 1|int main(int argc, char *argv[]) {
15| 1| MY_LOG("%d, %s, %d\n",
16| 1| MY_ID(argc),
17| 0| "a", // INCORRECT
18| 0| 1); // INCORRECT
19| 1|}
If the example is updated to avoid using MY_ID
withing MY_LOG
, the problem disappears (even if the updated line has a branch):
$ ./build-c.sh
clang version 19.1.7
1| |#include <stdio.h>
2| |
3| |int enabled = 0;
4| |
5| |#define MY_ID(x) 7
6| |
7| |#define MY_LOG(fmt, ...) \
8| 1| { \
9| 1| if (enabled) { \
10| 0| printf(fmt, ## __VA_ARGS__); \
11| 0| } \
12| 1| }
13| |
14| 1|int main(int argc, char *argv[]) {
15| 1| MY_LOG("%d, %s, %d\n",
16| 1| enabled ? 1 : 2, // Now the entire MY_LOG usage is covered
17| 1| "a",
18| 1| 1);
19| 1|}
I suspect this is specific to macro expansion because I can substitute other constructs, including function calls, in the place of MY_ID
and get correct results.
Also, I believe the ideal behavior here is that all lines within MY_LOG
usage in main
should be covered; we did execute that line and that's what usually happens. Then, appropriately, the MY_LOG
implementation lines should be marked uncovered.