diff --git a/scripts/test.sh b/scripts/test.sh index cdc6d3e1d..1b4873c9f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -267,6 +267,9 @@ bash "$ROOT/tests/test_activation_diagnostic_contract.sh" echo "=== Step 0e3: security gate fail-closed contract ===" bash "$ROOT/tests/test_security_gate_fail_closed.sh" +echo "=== Step 0e4: memory-analyzer gate warning-suffix contract (#2442) ===" +bash "$ROOT/tests/test_lint_mem_gate.sh" + echo "=== Step 0f: tree-sitter runtime Makefile dependencies ===" bash "$ROOT/tests/test_makefile_ts_runtime_dependencies.sh" diff --git a/tests/test_lint_mem_gate.sh b/tests/test_lint_mem_gate.sh index 05c2bd4a3..c6a655222 100755 --- a/tests/test_lint_mem_gate.sh +++ b/tests/test_lint_mem_gate.sh @@ -1,20 +1,56 @@ #!/usr/bin/env bash -# Regression contract for clang-tidy's `,-warnings-as-errors` diagnostic suffix. +# Regression contract for clang-tidy's `,-warnings-as-errors` diagnostic suffix +# (#2442). With WarningsAsErrors enabled, clang-tidy prints an analyzer check as +# [clang-analyzer-,-warnings-as-errors]; a gate that only accepts +# [] drops every such line and reports a clean gate. A suffixed finding +# WITH an argued whitelist entry must be counted under its plain check name and +# suppressed (exit 0, "1 argued false positive"). Every assertion is reported, +# so a regression names what broke. +# +# The gate runs from inside the fixture directory with RELATIVE paths. On the +# Windows leg python3 is a native MSYS2 build: MSYS converts path ARGUMENTS for +# it, but never the diagnostic text on stdin, so an absolute /c/... path in a +# finding would not resolve there, and a C:/... one would not parse (the file +# field ends at the first colon). set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +GATE="$ROOT/scripts/lint-mem-gate.py" FIX="$(mktemp -d "${TMPDIR:-/tmp}/lint-mem-gate.XXXXXX")" trap 'rm -rf "$FIX"' EXIT +failures=0 +fail() { + echo "FAIL: $*" >&2 + failures=$((failures + 1)) +} + +show() { + sed 's/^/ | /' "$1" >&2 +} + +# A subshell, so the test's own shell never sits inside $FIX (Windows cannot +# remove a directory that is a process's working directory). +run_gate() { + (cd "$FIX" && LINT_MEM_WHITELIST=whitelist.txt python3 "$GATE" "$@") +} + cat > "$FIX/sample.c" <<'EOF' int sample(int *value) { return *value; } EOF +# A native Windows python3 writes CRLF; strip it before the hash is reused. +HASH="$(run_gate --hash sample.c sample | tr -d '\r' | awk '/^segment-sha256:/ {print $2}')" +if [[ ! "$HASH" =~ ^[0-9a-f]{64}$ ]]; then + echo "FAIL: lint-mem-gate.py --hash sample.c sample printed no segment hash (got '$HASH')" >&2 + exit 1 +fi + cat > "$FIX/whitelist.txt" < /dev/null 2> "$FIX/whitelisted.err" || rc=$? +if [ "$rc" -ne 0 ]; then + fail "a whitelisted suffixed finding left the gate at exit $rc, expected 0" + show "$FIX/whitelisted.err" +fi +if ! grep -q 'memory gate clean (1 argued false positive' "$FIX/whitelisted.err"; then + fail "a whitelisted suffixed finding was not counted as 1 argued false positive" + show "$FIX/whitelisted.err" +fi -printf '%s\n' "$FIX/sample.c:2:12: warning: possible null dereference [clang-analyzer-core.NullDereference,-warnings-as-errors]" \ - | LINT_MEM_WHITELIST="$FIX/whitelist.txt" python3 "$ROOT/scripts/lint-mem-gate.py" 2> "$FIX/stderr" - -grep -q 'memory gate clean (1 argued false positive' "$FIX/stderr" +if [ "$failures" -ne 0 ]; then + echo "lint-mem-gate warning suffix contract: $failures assertion(s) failed" >&2 + exit 1 +fi echo 'lint-mem-gate warning suffix contract passed'