test(lint-mem): run the warning-suffix gate test on every leg

scripts/test.sh lists every shell contract test explicitly and does
not glob tests/, so tests/test_lint_mem_gate.sh never ran on any leg.
Register it as Step 0e4, next to the other gate contract (Step 0e3),
in the same two-line shape as its neighbours. Step 0 runs in every
default scripts/test.sh leg (macOS, Linux, Windows) and in the PR
contracts-only job. The wiring, Windows included, is a maintainer
decision (R1).

Make the test portable to the Windows leg. There python3 is a native
MSYS2 build: MSYS converts path arguments for it, but never the
diagnostic text on stdin, so the absolute fixture path inside the
finding does not resolve there, and a C:/ path would not parse (the
file field of FINDING_RE ends at the first colon). The gate now runs
from inside the fixture directory, in a subshell, with relative paths.
CRLF from a native python3 is stripped from the --hash output, the
whitelist is written once the hash is known (no placeholder rewrite),
the gate exit status is asserted, and every failed assertion is
reported together with the gate output.

Refs #2442

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-11-02 01:11:32 +01:00
1 parent b11f542109
commit 91070cb2f0
2 files changed
+59 -15

No files matched your search

+3
View File
@@ -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"
+56 -15
View File
@@ -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-<check>,-warnings-as-errors]; a gate that only accepts
# [<check>] 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" <<EOF
## $FIX/sample.c :: sample :: clang-analyzer-core.NullDereference
segment-sha256: PLACEHOLDER
## sample.c :: sample :: clang-analyzer-core.NullDereference
segment-sha256: $HASH
why: |
The test deliberately models a diagnostic that is suppressed by the fixture whitelist.
This text is long enough to exercise the gate's normal argument validation path.
@@ -22,18 +58,23 @@ tried: |
The fixture is intentionally minimal and only verifies diagnostic parsing.
EOF
HASH="$(LINT_MEM_WHITELIST="$FIX/whitelist.txt" python3 "$ROOT/scripts/lint-mem-gate.py" --hash "$FIX/sample.c" sample | awk '/^segment-sha256:/ {print $2}')"
python3 - "$FIX/whitelist.txt" "$HASH" <<'PY'
from pathlib import Path
import sys
SUFFIX='[clang-analyzer-core.NullDereference,-warnings-as-errors]'
path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8").replace("PLACEHOLDER", sys.argv[2])
path.write_text(text, encoding="utf-8")
PY
# Whitelisted: counted under the plain check name, then suppressed.
rc=0
printf '%s\n' "sample.c:2:12: warning: possible null dereference $SUFFIX" \
| run_gate > /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'