Skip to content

[coverage] incorrect coverage for lines following conditional terminating statements #144416

@justincady

Description

@justincady
Contributor

Code coverage reporting is incorrect in situations like this:

// coverage.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    {
        if (argc > 2) {
            return 2;
        }
        printf("one\n");
    }
    printf("two\n");
    return 0;
}

The expectation is without additional arguments both "one" and "two" will be printed and both print statements will be reported as covered, but:

# build.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 incorrectly marks the second printf statement as uncovered:

$ ./build.sh
clang version 21.0.0
one
two
    1|       |#include <stdio.h>
    2|       |
    3|       |int main(int argc, char *argv[])
    4|      1|{
    5|      1|    {
    6|      1|        if (argc > 2) {
    7|      0|            return 2;
    8|      0|        }
    9|      1|        printf("one\n");
   10|      1|    }
   11|      0|    printf("two\n"); // INCORRECT
   12|      1|    return 0;
   13|      1|}

It appears related to the region-terminating statement in the conditional. The same behavior appears with abort() or goto instead of return 2, for example. But the behavior disappears if the statement is non-region-terminating:

$ ./build.sh
clang version 21.0.0
one
two
    1|       |#include <stdio.h>
    2|       |
    3|       |int main(int argc, char *argv[])
    4|      1|{
    5|      1|    {
    6|      1|        if (argc > 2) {
    7|      0|            printf("zero\n"); // NO LONGER TERMINATING
    8|      0|        }
    9|      1|        printf("one\n");
   10|      1|    }
   11|      1|    printf("two\n"); // NOW CORRECT
   12|      1|    return 0;
   13|      1|}

I have not narrowed down if this is a region mapping issue versus an llvm-cov reporting issue.

Activity

justincady

justincady commented on Jun 16, 2025

@justincady
ContributorAuthor

One additional note: the problem also disappears when the extra braces are removed. This occurs in both C and C++.

$ ./build.sh
clang version 21.0.0
one
two
    1|       |#include <stdio.h>
    2|       |
    3|       |int main(int argc, char *argv[])
    4|      1|{
    5|       |
    6|      1|        if (argc > 2) {
    7|      0|            return 2;
    8|      0|        }
    9|      1|        printf("one\n");
   10|       |
   11|      1|    printf("two\n");
   12|      1|    return 0;
   13|      1|}
justincady

justincady commented on Jun 18, 2025

@justincady
ContributorAuthor

This is the mapping:

main:
  File 0, 4:1 -> 13:2 = #0
  File 0, 6:13 -> 6:21 = #0
  Branch,File 0, 6:13 -> 6:21 = #1, (#0 - #1)
  Gap,File 0, 6:22 -> 6:23 = #1
  File 0, 6:23 -> 8:10 = #1
  Gap,File 0, 8:10 -> 9:9 = (#0 - #1)
  File 0, 9:9 -> 12:13 = (#0 - #1)
  Gap,File 0, 10:6 -> 11:5 = 0

It appears the problem is the final gap region (10:6) being used for the computed line count of line 11. That seems incorrect. There is a valid gap region on the line, but conceptually shouldn't it be superceded by File 0, 9:9 -> 12:13 = (#0 - #1), a non-gap region on the line?

That counter for that region would be calculated as 1 - 0 = 1 (the correct value for line 11 if the early return is not taken).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @justincady

        Issue actions

          [coverage] incorrect coverage for lines following conditional terminating statements · Issue #144416 · llvm/llvm-project