#!/usr/bin/env bash # scripts/quality/cppcheck.sh # # Run cppcheck over the public headers. Complementary to clang-tidy: # cppcheck has different heuristics, fewer false-positives on heavy # template code (CGAL/Eigen), and catches some bugs (unused includes, # memory leaks in detail/) that clang-tidy is bad at. # # Local-only. Promotion to CI when the existing tree is finding-free # at the chosen severity level. # # Usage: # bash scripts/quality/cppcheck.sh # error+warning only # bash scripts/quality/cppcheck.sh --strict # +style, exit 1 on any # bash scripts/quality/cppcheck.sh --all # absolute everything, # useful for diffs only # # Exit codes: # 0 no findings at the chosen severity, or findings but not --strict # 1 findings + --strict # 2 prerequisite missing set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$ROOT" || exit 2 command -v cppcheck >/dev/null 2>&1 || { echo "FAIL: cppcheck not in PATH." >&2 echo " macOS: brew install cppcheck" >&2 echo " Linux: sudo apt install cppcheck" >&2 exit 2 } STRICT=0 ALL=0 for arg in "$@"; do case "$arg" in --strict) STRICT=1 ;; --all) ALL=1 ;; *) echo "Unknown arg: $arg" >&2; exit 2 ;; esac done ENABLE="warning" if [ "$STRICT" -eq 1 ]; then ENABLE="warning,style"; fi if [ "$ALL" -eq 1 ]; then ENABLE="all"; fi BUILD_DIR="build-cppcheck" LOG="$BUILD_DIR/cppcheck.log" mkdir -p "$BUILD_DIR" echo "cppcheck ($(cppcheck --version 2>&1 | head -1))" echo " enable: $ENABLE" echo " log: $LOG" echo # Suppress noise classes that are not actionable in our project: # missingIncludeSystem — CGAL/Eigen/Boost headers are intentionally # included implicitly; cppcheck cannot resolve. # unmatchedSuppression — cosmetic. # unusedFunction — header-only; many `inline` helpers ARE used, # cppcheck can't see across TUs. # normalCheckLevelMaxBranches — informational, not a finding. # # We point cppcheck at code/include/ only. The deps tree is third-party # code and out of scope. cppcheck \ --enable="$ENABLE" \ --std=c++17 \ --quiet \ --error-exitcode=2 \ --inline-suppr \ --suppress=missingIncludeSystem \ --suppress=unmatchedSuppression \ --suppress=unusedFunction \ --suppress=normalCheckLevelMaxBranches \ -I code/include \ code/include 2>&1 | tee "$LOG" rc=$? echo echo "── Summary ──" n=$(grep -cE "\[(error|warning|style|performance|portability)\]" "$LOG" || true) echo " total findings: $n" echo " full log: $LOG" if [ "$STRICT" -eq 1 ] && [ "$n" -gt 0 ]; then exit 1 fi if [ "$rc" -eq 2 ] && [ "$STRICT" -ne 1 ]; then # cppcheck signalled "error" severity but caller didn't ask --strict. echo echo "NOTE: cppcheck reported an `error`-severity finding. Even" echo " without --strict, please review the log." fi exit 0