Skip to content

check_coverage_roots.sh false-positives on Homebrew's non-isystem unixodbc include path (macOS) #438

Description

@Yaraslaut

Verification status

Reproduced and root-caused, measured on macOS (Darwin 25.6.0, arm64, Homebrew's unixodbc 2.3.14), while working the framework coverage/mutation improvement plan. Not verified on Linux CI, where the package manager's odbc.pc layout differs and this specific path is not expected to reproduce — the mechanism below is macOS/Homebrew-specific.

Symptom

scripts/coverage.sh fails its check_coverage_roots.sh gate (added for morph#426, to catch a compiler cache serving objects compiled in a different worktree):

check_coverage_roots: 3 of 614 files in the coverage mapping are not
  under <this worktree's absolute path>:
    /opt/homebrew/Cellar/unixodbc/2.3.14/include/sql.h
    /opt/homebrew/Cellar/unixodbc/2.3.14/include/sqlext.h
    /opt/homebrew/Cellar/unixodbc/2.3.14/include/sqlucode.h

Ruled out the gate's actual target (a stale compiler cache) before treating this as a false positive: neither ccache nor sccache is installed on this machine (which finds neither), and build/clang-coverage/CMakeCache.txt has no USE_COMPILER_CACHE entry at all — no cache was ever configured for this build.

Root cause

pkg-config --cflags odbc on this machine's Homebrew install emits -I/opt/homebrew/Cellar/unixodbc/2.3.14/include — a plain -I, not -isystem, because Homebrew's odbc.pc sets includedir to the versioned Cellar path rather than the /opt/homebrew/include symlink, and that Cellar path is not part of Xcode clang's implicit system search list (confirmed with clang++ -E -v -). The Lightweight dependency's CMake picks this up via pkg_check_modules/find_package(ODBC) without marking it SYSTEM, so sql.h/sqlext.h/sqlucode.h get real (non--isystem) coverage instrumentation and land in the unfiltered llvm-cov mapping that check_coverage_roots.sh checks — outside the checkout root, tripping the gate.

This is harmless to the actual uploaded report, confirmed by reading scripts/coverage.sh in full: its real report is scoped by the SOURCES=(include/morph examples/...) array (from line ~156), applied to llvm-cov show/report/export, and none of the 3 ODBC paths match any SOURCES entry — they would be silently absent from coverage.lcov regardless of whether this gate passes or fails. The gate is failing on a file that was never going to appear in the report either way.

What would change the verdict

Someone reproducing check_coverage_roots.sh's gate tripping on a header that does affect the actual filtered report (e.g. a SOURCES-matched path outside the checkout) would confirm the gate is still doing its real job correctly elsewhere — this report is only about the specific case of a non-SOURCES-matched foreign path.

Suggested fixes (not attempted here — needs a source change, out of scope for the task that found this)

  1. Mark the ODBC include path SYSTEM in the CMake that consumes it (wherever Lightweight's pkg_check_modules/find_package(ODBC) result is added to a target — a -isystem include is excluded from coverage instrumentation entirely, which also matches how every other genuinely-system header in this build is already treated), or
  2. Teach check_coverage_roots.sh to distinguish "foreign worktree path" (the real morph#426 threat model — a stale cache serving objects built in a different checkout) from "real vendor header with no SOURCES match" (provably harmless to the report, as shown above) — e.g. by cross-checking each out-of-root path against the SOURCES array before failing.

Option 1 seems preferable: it fixes the root cause (the header genuinely shouldn't be instrumented for coverage at all, matching every other system dependency) rather than special-casing the gate around a symptom.

Activity

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

    area: ciSubsystem: cibugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions