Issue 140893
Summary [coverage] nested macro with unused branch leads to incorrect coverage
Labels new issue
Assignees
Reporter justincady
    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=""
```

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.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to