@boyingliu01/xp-gate 0.31.3 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/java.sh +256 -4
- package/adapters/powershell.sh +45 -6
- package/bin/xp-gate.js +8 -2
- package/build-integrity/__tests__/gate-10.test.ts +88 -1
- package/build-integrity/gate-10.ts +51 -16
- package/dsh.bundle.patch.yml +15 -0
- package/gate-13-compiler-strictness.sh +1031 -0
- package/gate-3.sh +48 -13
- package/hooks/gate-13-compiler-strictness.sh +1031 -0
- package/hooks/gate-3.sh +48 -13
- package/hooks/lib/coverage-total.sh +18 -2
- package/hooks/lib/java-test-selection.sh +251 -0
- package/hooks/lib/jscpd-run.sh +9 -0
- package/hooks/lib/validate-code-walkthrough.cjs +186 -29
- package/hooks/pre-commit +269 -13
- package/lib/__tests__/detect-deps-dsh-profile.test.js +106 -0
- package/lib/__tests__/doctor-tui-skill-freshness.test.js +140 -0
- package/lib/__tests__/gate-runner.test.js +29 -2
- package/lib/__tests__/issue-586-principles-cmdline.test.js +35 -0
- package/lib/__tests__/issue-618-gate-not-run.test.js +100 -0
- package/lib/__tests__/shared-utils.test.js +21 -0
- package/lib/check.js +6 -2
- package/lib/detect-deps.js +42 -10
- package/lib/doctor-tui.js +111 -21
- package/lib/gate-runner.js +111 -37
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/principles.js +48 -6
- package/lib/shared-utils.js +8 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mock-policy/gate-m3.ts +57 -23
- package/mutation/AGENTS.md +3 -3
- package/mutation/__tests__/pitest-gradle-availability.test.ts +74 -0
- package/mutation/__tests__/test-filter-parity.test.ts +5 -2
- package/mutation/gate-m.ts +30 -24
- package/mutation/runners/mutmut-runner.ts +80 -30
- package/mutation/runners/pitest-runner.ts +60 -42
- package/package.json +14 -2
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/lib/command.d.ts +38 -0
- package/plugins/dsh/lib/command.d.ts.map +1 -0
- package/plugins/dsh/lib/command.js +87 -0
- package/plugins/dsh/lib/credentials.d.ts +39 -0
- package/plugins/dsh/lib/credentials.d.ts.map +1 -0
- package/plugins/dsh/lib/credentials.js +57 -0
- package/plugins/dsh/lib/delphi-config.d.ts +129 -0
- package/plugins/dsh/lib/delphi-config.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-config.js +369 -0
- package/plugins/dsh/lib/delphi-run.d.ts +24 -0
- package/plugins/dsh/lib/delphi-run.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-run.js +113 -0
- package/plugins/dsh/lib/delphi.d.ts +94 -0
- package/plugins/dsh/lib/delphi.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi.js +207 -0
- package/plugins/dsh/lib/dialect.d.ts +24 -0
- package/plugins/dsh/lib/dialect.d.ts.map +1 -0
- package/plugins/dsh/lib/dialect.js +36 -0
- package/plugins/dsh/lib/evidence.d.ts +70 -0
- package/plugins/dsh/lib/evidence.d.ts.map +1 -0
- package/plugins/dsh/lib/evidence.js +92 -0
- package/plugins/dsh/lib/gate-runner.d.ts +49 -0
- package/plugins/dsh/lib/gate-runner.d.ts.map +1 -0
- package/plugins/dsh/lib/gate-runner.js +95 -0
- package/plugins/dsh/lib/git-context.d.ts +26 -0
- package/plugins/dsh/lib/git-context.d.ts.map +1 -0
- package/plugins/dsh/lib/git-context.js +30 -0
- package/plugins/dsh/lib/hooks.d.ts +42 -0
- package/plugins/dsh/lib/hooks.d.ts.map +1 -0
- package/plugins/dsh/lib/hooks.js +113 -0
- package/plugins/dsh/lib/index.d.ts +20 -0
- package/plugins/dsh/lib/index.d.ts.map +1 -0
- package/plugins/dsh/lib/index.js +430 -0
- package/plugins/dsh/lib/skill-provider.d.ts +15 -0
- package/plugins/dsh/lib/skill-provider.d.ts.map +1 -0
- package/plugins/dsh/lib/skill-provider.js +205 -0
- package/plugins/dsh/lib/sprint.d.ts +60 -0
- package/plugins/dsh/lib/sprint.d.ts.map +1 -0
- package/plugins/dsh/lib/sprint.js +92 -0
- package/plugins/dsh/package.json +14 -8
- package/plugins/dsh/scripts/verify-skill-provider.mjs +113 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/src/command-single-form.test.ts +67 -0
- package/plugins/dsh/src/command.test.ts +21 -17
- package/plugins/dsh/src/command.ts +19 -21
- package/plugins/dsh/src/compat.test.ts +200 -0
- package/plugins/dsh/src/contract.test.ts +7 -1
- package/plugins/dsh/src/credentials.test.ts +93 -0
- package/plugins/dsh/src/credentials.ts +71 -0
- package/plugins/dsh/src/delphi-cancellation.test.ts +71 -0
- package/plugins/dsh/src/delphi-config-apikey-override.test.ts +94 -0
- package/plugins/dsh/src/delphi-config-credential-refs.test.ts +75 -0
- package/plugins/dsh/src/delphi-config.ts +146 -53
- package/plugins/dsh/src/delphi-run.ts +27 -4
- package/plugins/dsh/src/delphi.ts +133 -65
- package/plugins/dsh/src/evidence.test.ts +74 -0
- package/plugins/dsh/src/evidence.ts +141 -0
- package/plugins/dsh/src/gate-runner.test.ts +14 -6
- package/plugins/dsh/src/gate-runner.ts +68 -5
- package/plugins/dsh/src/git-context.test.ts +39 -0
- package/plugins/dsh/src/git-context.ts +38 -0
- package/plugins/dsh/src/hooks.test.ts +87 -0
- package/plugins/dsh/src/hooks.ts +126 -0
- package/plugins/dsh/src/index.test.ts +63 -6
- package/plugins/dsh/src/index.ts +340 -13
- package/plugins/dsh/src/skill-provider.test.ts +194 -0
- package/plugins/dsh/src/skill-provider.ts +231 -0
- package/plugins/dsh/src/sprint.test.ts +64 -0
- package/plugins/dsh/src/sprint.ts +122 -0
- package/plugins/dsh/src/tri-state.test.ts +123 -0
- package/plugins/opencode/index.ts +192 -58
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/principles/__tests__/analyze-read-failure.test.ts +70 -0
- package/principles/__tests__/baseline-entry-total-warnings.test.ts +76 -0
- package/principles/__tests__/baseline-prune.test.ts +61 -0
- package/principles/__tests__/cli-files-separators.test.ts +69 -0
- package/principles/__tests__/config-merge-warnings.test.ts +148 -0
- package/principles/adapters/__tests__/cpp-code-block.test.ts +123 -0
- package/principles/adapters/__tests__/cpp-scanner-equivalence.test.ts +67 -0
- package/principles/adapters/__tests__/typescript-block-comment-lines.test.ts +53 -0
- package/principles/adapters/__tests__/typescript-masking.test.ts +96 -0
- package/principles/adapters/cpp.ts +102 -50
- package/principles/adapters/java.ts +22 -3
- package/principles/adapters/typescript.ts +548 -178
- package/principles/analyzer.ts +36 -3
- package/principles/baseline-prune.ts +19 -7
- package/principles/baseline.ts +58 -70
- package/principles/config.ts +56 -35
- package/principles/index.ts +122 -40
- package/principles/rules/clean-code/deep-nesting.ts +36 -22
- package/principles/rules/clean-code/unused-imports.ts +30 -19
- package/principles/rules/solid/dip.ts +30 -20
- package/principles/rules/solid/lsp.ts +17 -2
- package/principles/rules/solid/ocp.ts +33 -23
- package/principles/rules/solid/srp.ts +65 -23
- package/scripts/delphi-external-review.cjs +25 -2
- package/scripts/sync-package-content.js +28 -1
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# GATE 13: Compiler Strictness Check
|
|
4
|
+
#
|
|
5
|
+
# The compiler is the first and cheapest static analysis tool for any compiled
|
|
6
|
+
# language, and XP-Gate historically never used it that way: `cpp.sh` only runs
|
|
7
|
+
# clang-tidy/cppcheck over the first 20 files, and Gate 9's build-integrity half
|
|
8
|
+
# is TypeScript-and-Maven only. A measured `g++ -Wall -Wextra` pass over an
|
|
9
|
+
# innocuous-looking translation unit reports -Wnon-virtual-dtor, -Wreorder,
|
|
10
|
+
# -Wparentheses, -Wclass-memaccess, -Wold-style-cast and -Wswitch-default —
|
|
11
|
+
# real defects no linter in the chain is positioned to see. Bare `javac`
|
|
12
|
+
# likewise reports NOTHING for raw types; `-Xlint:all` does, and an identical
|
|
13
|
+
# Java file went from "0 diagnostics" to a hard error under `-Werror`.
|
|
14
|
+
#
|
|
15
|
+
# Design: docs/plans/2026-10-26-gate-13-compiler-strictness-design.md
|
|
16
|
+
# Requirement rounds 1-3 (.sprint-state/) — review findings folded in below are
|
|
17
|
+
# tagged with their review id (CI-xx architecture, MC-xx technical, FC-xx
|
|
18
|
+
# feasibility).
|
|
19
|
+
#
|
|
20
|
+
# ── VERDICT CONTRACT ────────────────────────────────────────────────────────
|
|
21
|
+
# Inherited from pre-commit's established java.sh / powershell.sh protocol (#507):
|
|
22
|
+
# 0 = PASS every compiled file clean or within its baseline
|
|
23
|
+
# 1 = FAIL new warnings on changed files (a real finding) -> commit blocked
|
|
24
|
+
# 2 = BLOCK tool RAN but its output is untrustworthy (fail-closed)
|
|
25
|
+
# 3 = WARN compiler absent / cross-compiling / timeout / warn-only findings
|
|
26
|
+
# -> counted IN the scoring denominator, never PASS
|
|
27
|
+
#
|
|
28
|
+
# A TIMEOUT is always 3, never 2 (architecture MA-03): a timeout is incomplete
|
|
29
|
+
# coverage, not a wrong conclusion, and promoting it to BLOCK would turn machine
|
|
30
|
+
# load into a failed commit. 2 is reserved for the case where the tool finished
|
|
31
|
+
# but contradicted itself or emitted unparseable output.
|
|
32
|
+
#
|
|
33
|
+
# Reasons cross the command-substitution subshell as an `XP_G13_REASON: ...`
|
|
34
|
+
# stdout line — an `export` inside this file can NEVER reach the caller, whose
|
|
35
|
+
# `$(...)` discards the child environment.
|
|
36
|
+
# ============================================================================
|
|
37
|
+
|
|
38
|
+
# ── Verdict codes (pinned by the bats suite) ────────────────────────────────
|
|
39
|
+
# shellcheck disable=SC2034 # public contract: pre-commit reads these by name
|
|
40
|
+
GATE13_PASS=0
|
|
41
|
+
# shellcheck disable=SC2034 # emitted from SLICE-2 onward (C/C++ warning findings)
|
|
42
|
+
GATE13_FAIL=1
|
|
43
|
+
# shellcheck disable=SC2034 # public contract
|
|
44
|
+
GATE13_BLOCK=2
|
|
45
|
+
# shellcheck disable=SC2034 # public contract
|
|
46
|
+
GATE13_WARN=3
|
|
47
|
+
|
|
48
|
+
GATE13_DEFAULT_TIMEOUT=120
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Reason channel. Mirrors java.sh's XP_G1_REASON protocol so pre-commit can
|
|
52
|
+
# surface a SKIP/WARN cause without losing the verdict code.
|
|
53
|
+
#
|
|
54
|
+
# Written to stderr, NOT stdout: the helpers below are also called in command
|
|
55
|
+
# substitution (`budget=$(gate13_timeout_budget)`), and a stdout reason would be
|
|
56
|
+
# captured INTO the value — turning a valid budget into "XP_G13_REASON: ... 120"
|
|
57
|
+
# and breaking every numeric consumer. pre-commit reads the reason with 2>&1.
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
gate13_reason() {
|
|
60
|
+
printf 'XP_G13_REASON: %s\n' "$*" >&2
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# AC-007-01 — timeout budget.
|
|
65
|
+
# Precedence (feasibility FC-02): XP_GATE_COMPILER_TIMEOUT > .xp-gate/compiler-gate-timeout
|
|
66
|
+
# > 120s. The env var wins so CI can raise the budget for a large changeset
|
|
67
|
+
# without editing a tracked file. An unreadable/invalid override WARNs and falls
|
|
68
|
+
# back — an unreadable override must never silently disable the budget.
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
gate13_timeout_budget() {
|
|
71
|
+
local file=".xp-gate/compiler-gate-timeout" val=""
|
|
72
|
+
|
|
73
|
+
if [ -n "${XP_GATE_COMPILER_TIMEOUT:-}" ]; then
|
|
74
|
+
val=$(printf '%s' "$XP_GATE_COMPILER_TIMEOUT" | tr -d '[:space:]')
|
|
75
|
+
case "$val" in
|
|
76
|
+
''|*[!0-9]*|0)
|
|
77
|
+
gate13_reason "invalid XP_GATE_COMPILER_TIMEOUT='${XP_GATE_COMPILER_TIMEOUT}' (expected a positive integer), falling back to ${GATE13_DEFAULT_TIMEOUT}s"
|
|
78
|
+
echo "$GATE13_DEFAULT_TIMEOUT"
|
|
79
|
+
return 0
|
|
80
|
+
;;
|
|
81
|
+
*) echo "$val"; return 0 ;;
|
|
82
|
+
esac
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [ -f "$file" ]; then
|
|
86
|
+
val=$(tr -d '[:space:]' < "$file")
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
case "$val" in
|
|
90
|
+
'')
|
|
91
|
+
echo "$GATE13_DEFAULT_TIMEOUT"
|
|
92
|
+
;;
|
|
93
|
+
*[!0-9]*|0)
|
|
94
|
+
gate13_reason "invalid $file value '${val}' (expected a positive integer), falling back to ${GATE13_DEFAULT_TIMEOUT}s"
|
|
95
|
+
echo "$GATE13_DEFAULT_TIMEOUT"
|
|
96
|
+
;;
|
|
97
|
+
*) echo "$val" ;;
|
|
98
|
+
esac
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# AC-007-02 / AC-006-03 — timeout classification. Always WARN, and the output
|
|
103
|
+
# must be visually distinguishable from "clean" so a reader never mistakes
|
|
104
|
+
# incomplete coverage for a pass.
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
gate13_classify_timeout() {
|
|
107
|
+
echo "TIMEOUT — coverage incomplete (the compiler did not finish within the budget); verdict treated as WARN, not a finding"
|
|
108
|
+
return "$GATE13_WARN"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
# AC-006-03 — the ONLY path that yields verdict 2. The tool ran, but produced
|
|
113
|
+
# output that contradicts itself or cannot be interpreted, so no conclusion can
|
|
114
|
+
# be drawn. Missing tools never come here (they are 3).
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
gate13_classify_unusable() {
|
|
117
|
+
echo "BLOCK (fail-closed) — the compiler ran but its output is untrustworthy; refusing to report success"
|
|
118
|
+
return "$GATE13_BLOCK"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# AC-001-03 — cross-compilation predicate. Pure: takes a compiler name/command,
|
|
123
|
+
# returns 0 (cross) or 1 (host). Three explicit conditions (architecture CI-01),
|
|
124
|
+
# each independently testable:
|
|
125
|
+
# 1. CROSS_COMPILE is non-empty
|
|
126
|
+
# 2. the basename matches ^(prefix-)?(gcc|g++|cc|c++|clang|clang++)(-ver)?$
|
|
127
|
+
# with a NON-EMPTY prefix (arm-linux-gnueabihf-g++, aarch64-none-elf-gcc)
|
|
128
|
+
# 3. the target triple differs from the host triple (needs -dumpmachine)
|
|
129
|
+
#
|
|
130
|
+
# Explicitly answered in review: an empty CROSS_COMPILE with
|
|
131
|
+
# CC=arm-linux-gnueabihf-gcc IS cross-compilation — condition 2 catches it.
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
gate13_is_cross_compiling() {
|
|
134
|
+
local cc="${1:-}"
|
|
135
|
+
local base prefix
|
|
136
|
+
|
|
137
|
+
if [ -n "${CROSS_COMPILE:-}" ]; then
|
|
138
|
+
return 0
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
base=$(basename -- "$cc" 2>/dev/null || printf '%s' "$cc")
|
|
142
|
+
# Strip a trailing -<version> / -<major.minor> so g++-13 is host, not cross.
|
|
143
|
+
base=$(printf '%s' "$base" | sed -E 's/-[0-9]+(\.[0-9]+)*$//')
|
|
144
|
+
|
|
145
|
+
# prefix-<tool>. `gcc` itself has no prefix; `<prefix>-gcc` does.
|
|
146
|
+
if printf '%s' "$base" | grep -Eq '^(.+)-(gcc|g\+\+|cc|c\+\+|clang|clang\+\+)$'; then
|
|
147
|
+
prefix=$(printf '%s' "$base" | sed -E 's/^(.+)-(gcc|g\+\+|cc|c\+\+|clang|clang\+\+)$/\1/')
|
|
148
|
+
# A leading path component was already stripped by basename, so any prefix
|
|
149
|
+
# left here is a toolchain prefix.
|
|
150
|
+
if [ -n "$prefix" ]; then
|
|
151
|
+
return 0
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Known host-style names with no prefix at all.
|
|
156
|
+
case "$base" in
|
|
157
|
+
gcc|g++|cc|c++|clang|clang++) return 1 ;;
|
|
158
|
+
esac
|
|
159
|
+
|
|
160
|
+
# Anything else (e.g. an absolute path to a foreign toolchain) — cond 3.
|
|
161
|
+
if [ -x "$cc" ] && command -v "$cc" >/dev/null 2>&1; then
|
|
162
|
+
local target host
|
|
163
|
+
target=$("$cc" -dumpmachine 2>/dev/null)
|
|
164
|
+
host=$(uname -m 2>/dev/null)
|
|
165
|
+
if [ -n "$target" ] && [ -n "$host" ]; then
|
|
166
|
+
case "$target" in
|
|
167
|
+
"$host"*) return 1 ;;
|
|
168
|
+
*) return 0 ;;
|
|
169
|
+
esac
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
return 1
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# Usability probe — `command -v` alone is NOT enough. A broken shim, a missing
|
|
178
|
+
# shared library, or a PATH entry for the wrong architecture all pass `command
|
|
179
|
+
# -v` and then fail at the first real invocation. Treating that as "compiler
|
|
180
|
+
# present" would route to the analysis stage and report a spurious failure (or,
|
|
181
|
+
# worse, no failure at all). Ask the compiler to identify itself; anything but
|
|
182
|
+
# a clean exit means "not usable".
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
gate13_compiler_usable() {
|
|
185
|
+
local cc="$1"
|
|
186
|
+
"$cc" --version >/dev/null 2>&1
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
# AC-001-01 / AC-001-04 — compiler selection.
|
|
191
|
+
# Order (REQ-001): $CXX > Makefile CXX := > PATH (g++ > clang++ > c++).
|
|
192
|
+
# A user-named compiler that cannot be used is REPORTED, never silently replaced
|
|
193
|
+
# (AC-001-04) — a silent swap would compile the wrong code with the wrong flags
|
|
194
|
+
# and still print PASS.
|
|
195
|
+
#
|
|
196
|
+
# Prints the selected compiler path on stdout and the selection basis on stderr
|
|
197
|
+
# so callers can capture the path without the narrative.
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
gate13_select_cxx() {
|
|
200
|
+
local cand=""
|
|
201
|
+
|
|
202
|
+
# 1. Environment — this is what make itself would use.
|
|
203
|
+
if [ -n "${CXX:-}" ]; then
|
|
204
|
+
if command -v "$CXX" >/dev/null 2>&1 && gate13_compiler_usable "$CXX"; then
|
|
205
|
+
echo "compiler selected: $CXX (basis: \$CXX environment variable)" >&2
|
|
206
|
+
printf '%s\n' "$CXX"
|
|
207
|
+
return 0
|
|
208
|
+
fi
|
|
209
|
+
gate13_reason "explicitly configured CXX='${CXX}' is not runnable; refusing to substitute a different compiler"
|
|
210
|
+
echo "compiler selected: none (basis: \$CXX='${CXX}' is unusable)" >&2
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# 2. Makefile assignment — the project's own declared compiler.
|
|
215
|
+
if [ -f "Makefile" ]; then
|
|
216
|
+
cand=$(sed -nE 's/^[[:space:]]*CXX[[:space:]]*[:?]?=[[:space:]]*([^[:space:]#]+).*/\1/p' Makefile 2>/dev/null | head -1)
|
|
217
|
+
if [ -n "$cand" ] && command -v "$cand" >/dev/null 2>&1 && gate13_compiler_usable "$cand"; then
|
|
218
|
+
echo "compiler selected: $cand (basis: Makefile CXX assignment)" >&2
|
|
219
|
+
printf '%s\n' "$cand"
|
|
220
|
+
return 0
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# 3. PATH lookup, most standard first.
|
|
225
|
+
for cand in g++ clang++ c++; do
|
|
226
|
+
if command -v "$cand" >/dev/null 2>&1 && gate13_compiler_usable "$cand"; then
|
|
227
|
+
echo "compiler selected: $cand (basis: PATH lookup)" >&2
|
|
228
|
+
printf '%s\n' "$cand"
|
|
229
|
+
return 0
|
|
230
|
+
fi
|
|
231
|
+
done
|
|
232
|
+
|
|
233
|
+
echo "compiler selected: none (basis: no usable C++ compiler on PATH)" >&2
|
|
234
|
+
return 1
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# Same order for C, so a pure-C project is not forced through a C++ driver.
|
|
238
|
+
gate13_select_cc() {
|
|
239
|
+
local cand=""
|
|
240
|
+
|
|
241
|
+
if [ -n "${CC:-}" ]; then
|
|
242
|
+
if command -v "$CC" >/dev/null 2>&1 && gate13_compiler_usable "$CC"; then
|
|
243
|
+
printf '%s\n' "$CC"
|
|
244
|
+
return 0
|
|
245
|
+
fi
|
|
246
|
+
gate13_reason "explicitly configured CC='${CC}' is not runnable; refusing to substitute a different compiler"
|
|
247
|
+
return 1
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
if [ -f "Makefile" ]; then
|
|
251
|
+
cand=$(sed -nE 's/^[[:space:]]*CC[[:space:]]*[:?]?=[[:space:]]*([^[:space:]#]+).*/\1/p' Makefile 2>/dev/null | head -1)
|
|
252
|
+
if [ -n "$cand" ] && command -v "$cand" >/dev/null 2>&1 && gate13_compiler_usable "$cand"; then
|
|
253
|
+
printf '%s\n' "$cand"
|
|
254
|
+
return 0
|
|
255
|
+
fi
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
for cand in gcc clang cc; do
|
|
259
|
+
if command -v "$cand" >/dev/null 2>&1 && gate13_compiler_usable "$cand"; then
|
|
260
|
+
printf '%s\n' "$cand"
|
|
261
|
+
return 0
|
|
262
|
+
fi
|
|
263
|
+
done
|
|
264
|
+
|
|
265
|
+
return 1
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
# AC-008-02 — emergency kill switch, orthogonal to the strictness tier
|
|
270
|
+
# (feasibility FC-03). A gate that cannot be disabled during a misreport storm
|
|
271
|
+
# gets disabled permanently by `--no-verify`, which the Code of Conduct forbids —
|
|
272
|
+
# so the sanctioned escape hatch is an explicit, audited SKIP.
|
|
273
|
+
# ---------------------------------------------------------------------------
|
|
274
|
+
gate13_is_disabled() {
|
|
275
|
+
[ "${XP_GATE_DISABLE_13:-}" = "1" ]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
# AC-002-01 — strictness tier resolution.
|
|
280
|
+
# Values: strict | balanced (default) | warn-only. An unrecognised value WARNs
|
|
281
|
+
# and falls back to balanced rather than silently choosing a stricter or looser
|
|
282
|
+
# policy than the user asked for.
|
|
283
|
+
# ---------------------------------------------------------------------------
|
|
284
|
+
gate13_resolve_tier() {
|
|
285
|
+
local tier="${1:-${XP_GATE_COMPILER_STRICTNESS:-balanced}}"
|
|
286
|
+
case "$tier" in
|
|
287
|
+
strict|balanced|warn-only) printf '%s\n' "$tier" ;;
|
|
288
|
+
*)
|
|
289
|
+
gate13_reason "unknown XP_GATE_COMPILER_STRICTNESS='${tier}' (expected strict|balanced|warn-only), falling back to balanced"
|
|
290
|
+
printf 'balanced\n'
|
|
291
|
+
;;
|
|
292
|
+
esac
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
# AC-002-01 — the flag set for a tier and language.
|
|
297
|
+
#
|
|
298
|
+
# balanced covers the highest-value real-bug classes from the GCC manual with a
|
|
299
|
+
# low false-positive cost. strict adds -Wconversion/-Wsign-conversion, which are
|
|
300
|
+
# very noisy in numeric code, hence opt-in. The C++-only flags are separated so a
|
|
301
|
+
# C translation unit is never handed -Wold-style-cast (a C++ diagnostic gcc would
|
|
302
|
+
# reject as unrecognised for C with -Werror).
|
|
303
|
+
# ---------------------------------------------------------------------------
|
|
304
|
+
gate13_cpp_flags() {
|
|
305
|
+
local tier lang base strict common
|
|
306
|
+
tier=$(gate13_resolve_tier "${1:-}")
|
|
307
|
+
lang="${2:-cpp}"
|
|
308
|
+
|
|
309
|
+
base="-Wall -Wextra"
|
|
310
|
+
|
|
311
|
+
if [ "$tier" = "balanced" ] || [ "$tier" = "warn-only" ]; then
|
|
312
|
+
printf '%s\n' "$base"
|
|
313
|
+
return 0
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# strict
|
|
317
|
+
common="-Wpedantic -Wshadow -Wformat=2 -Wcast-qual -Wcast-align -Wnull-dereference -Wswitch-enum"
|
|
318
|
+
if [ "$lang" = "c" ]; then
|
|
319
|
+
printf '%s %s\n' "$base" "$common"
|
|
320
|
+
else
|
|
321
|
+
printf '%s %s -Wconversion -Wsign-conversion -Wold-style-cast -Wuseless-cast -Wnon-virtual-dtor -Woverloaded-virtual\n' "$base" "$common"
|
|
322
|
+
fi
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# ---------------------------------------------------------------------------
|
|
326
|
+
# AC-002-02 / AC-002-03 — capability probe.
|
|
327
|
+
#
|
|
328
|
+
# One clean throwaway translation unit, each candidate flag tried ALONE in the
|
|
329
|
+
# POSITIVE form. Measured: gcc reports `unrecognized command-line option '-Wfoo'`
|
|
330
|
+
# for an unknown positive flag, but accepts an unknown `-Wno-foo` silently (a
|
|
331
|
+
# `cc1plus: note:` appears only when other diagnostics exist). Probing the
|
|
332
|
+
# negative form would therefore "succeed" for every flag and the gate would claim
|
|
333
|
+
# checks it never ran.
|
|
334
|
+
#
|
|
335
|
+
# Caching (architecture MA-01): results live in GATE13_PROBE_CACHE for the
|
|
336
|
+
# lifetime of one hook run — a persisted cache would go stale on compiler upgrade.
|
|
337
|
+
# Emits the ACCEPTED flags on stdout (space separated) and `dropped flag:` lines
|
|
338
|
+
# on stderr.
|
|
339
|
+
# ---------------------------------------------------------------------------
|
|
340
|
+
gate13_flag_supported() {
|
|
341
|
+
local cc="$1" flag="$2" lang="${3:-cpp}" probe_tu out
|
|
342
|
+
# The cache key carries the language (walkthrough R2 MA-05): gcc accepts
|
|
343
|
+
# -Wold-style-cast for C++ and rejects it for C, so a key without the language
|
|
344
|
+
# would let a C probe inherit the C++ answer.
|
|
345
|
+
local key="${cc}|${lang}|${flag}"
|
|
346
|
+
|
|
347
|
+
# Two separate caches, checked separately. A single "seen this key" cache was
|
|
348
|
+
# wrong (walkthrough MA-02): once any flag was dropped, every cached key
|
|
349
|
+
# returned "supported", re-enabling flags the compiler had already rejected.
|
|
350
|
+
case " ${GATE13_PROBE_DROPPED:-} " in
|
|
351
|
+
*" ${key} "*) return 1 ;;
|
|
352
|
+
esac
|
|
353
|
+
case " ${GATE13_PROBE_CACHE:-} " in
|
|
354
|
+
*" ${key} "*) return 0 ;;
|
|
355
|
+
esac
|
|
356
|
+
|
|
357
|
+
probe_tu="${GATE13_PROBE_TU:-}"
|
|
358
|
+
if [ -z "$probe_tu" ] || [ ! -f "$probe_tu" ]; then
|
|
359
|
+
probe_tu="${TMPDIR:-/tmp}/gate13_probe_$$.cpp"
|
|
360
|
+
printf 'int main() { return 0; }\n' > "$probe_tu"
|
|
361
|
+
GATE13_PROBE_TU="$probe_tu"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
if out=$("$cc" -fsyntax-only "$flag" "$probe_tu" 2>&1); then
|
|
365
|
+
GATE13_PROBE_CACHE="${GATE13_PROBE_CACHE:-} ${key}"
|
|
366
|
+
return 0
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
case "$out" in
|
|
370
|
+
*"unrecognized command-line option"*|*"unknown warning option"*|*"unknown argument"*)
|
|
371
|
+
GATE13_PROBE_DROPPED="${GATE13_PROBE_DROPPED:-} ${key}"
|
|
372
|
+
return 1
|
|
373
|
+
;;
|
|
374
|
+
esac
|
|
375
|
+
|
|
376
|
+
# The flag compiled but failed for some other reason (bad probe TU, missing
|
|
377
|
+
# system headers). Assume support: dropping a working flag silently loses
|
|
378
|
+
# coverage, and a genuinely unsupported flag is caught by the pattern above.
|
|
379
|
+
GATE13_PROBE_CACHE="${GATE13_PROBE_CACHE:-} ${key}"
|
|
380
|
+
return 0
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
gate13_probe_flags() {
|
|
384
|
+
local cc="$1" lang="$2"
|
|
385
|
+
shift 2
|
|
386
|
+
local flag accepted="" dropped=""
|
|
387
|
+
|
|
388
|
+
for flag in "$@"; do
|
|
389
|
+
if gate13_flag_supported "$cc" "$flag" "$lang"; then
|
|
390
|
+
accepted="$accepted $flag"
|
|
391
|
+
else
|
|
392
|
+
dropped="$dropped $flag"
|
|
393
|
+
printf 'dropped flag: %s (unsupported by %s)\n' "$flag" "$(gate13_compiler_id "$cc")" >&2
|
|
394
|
+
fi
|
|
395
|
+
done
|
|
396
|
+
|
|
397
|
+
printf '%s\n' "$(printf '%s' "$accepted" | sed 's/^ *//')"
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
gate13_compiler_id() {
|
|
401
|
+
local cc="$1" v
|
|
402
|
+
v=$("$cc" --version 2>/dev/null | head -1)
|
|
403
|
+
printf '%s' "${v:-$cc}"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# ---------------------------------------------------------------------------
|
|
407
|
+
# AC-003-04 — the changed set. Staged files only, matching Gate 1/12.
|
|
408
|
+
# Deleted files are excluded: they cannot be compiled.
|
|
409
|
+
# ---------------------------------------------------------------------------
|
|
410
|
+
gate13_changed_cpp_files() {
|
|
411
|
+
git diff --cached --name-only --diff-filter=d -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.C' 2>/dev/null
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
# Emits "<status>\t<file>" where status is A (added) or M (modified/other).
|
|
415
|
+
gate13_classify_changed() {
|
|
416
|
+
local f status
|
|
417
|
+
while IFS= read -r f; do
|
|
418
|
+
[ -n "$f" ] || continue
|
|
419
|
+
status=$(git diff --cached --name-status --diff-filter=ACMR -- "$f" 2>/dev/null | awk '{print $1}' | head -1)
|
|
420
|
+
case "$status" in
|
|
421
|
+
A) printf 'added\t%s\n' "$f" ;;
|
|
422
|
+
*) printf 'modified\t%s\n' "$f" ;;
|
|
423
|
+
esac
|
|
424
|
+
done <<EOF
|
|
425
|
+
$(gate13_changed_cpp_files)
|
|
426
|
+
EOF
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
# ---------------------------------------------------------------------------
|
|
430
|
+
# AC-003-02 — baseline storage. In-repo, mirroring Gate 6's .warnings-baseline.json
|
|
431
|
+
# precedent (technical CRITICAL-1 / feasibility FC-01). A local cache was
|
|
432
|
+
# rejected because it makes CI and developer verdicts diverge.
|
|
433
|
+
# The HOOK NEVER WRITES THIS FILE — that is the precise meaning of
|
|
434
|
+
# "baseline only grows" (architecture CI-03): a hook that silently rewrote the
|
|
435
|
+
# baseline could excuse its own regressions.
|
|
436
|
+
# ---------------------------------------------------------------------------
|
|
437
|
+
gate13_baseline_path() {
|
|
438
|
+
printf '%s\n' ".xp-gate/compiler-warnings-baseline.json"
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# Prints the recorded warning count for a file, or empty when there is no
|
|
442
|
+
# baseline / no entry for that file.
|
|
443
|
+
gate13_baseline_count() {
|
|
444
|
+
local file="$1" path count
|
|
445
|
+
path=$(gate13_baseline_path)
|
|
446
|
+
[ -f "$path" ] || return 0
|
|
447
|
+
|
|
448
|
+
if command -v node >/dev/null 2>&1; then
|
|
449
|
+
count=$(node -e '
|
|
450
|
+
const fs = require("fs");
|
|
451
|
+
try {
|
|
452
|
+
const j = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
453
|
+
const e = (j.files || {})[process.argv[2]];
|
|
454
|
+
if (e && typeof e.count === "number") process.stdout.write(String(e.count));
|
|
455
|
+
} catch (_) { /* unreadable baseline == no baseline */ }
|
|
456
|
+
' "$path" "$file" 2>/dev/null)
|
|
457
|
+
fi
|
|
458
|
+
printf '%s' "$count"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
gate13_baseline_exists() {
|
|
462
|
+
[ -f "$(gate13_baseline_path)" ]
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
# ---------------------------------------------------------------------------
|
|
466
|
+
# Compile one translation unit and count its diagnostics.
|
|
467
|
+
# -fsyntax-only: no artifacts, measured to write nothing into the tree.
|
|
468
|
+
# Prints the diagnostics on stderr; the COUNT goes to stdout.
|
|
469
|
+
# ---------------------------------------------------------------------------
|
|
470
|
+
gate13_count_warnings() {
|
|
471
|
+
local cc="$1" flags="$2" file="$3" out
|
|
472
|
+
# shellcheck disable=SC2086 # flags is a deliberately word-split flag list
|
|
473
|
+
out=$("$cc" -fsyntax-only $flags "$file" 2>&1)
|
|
474
|
+
printf '%s\n' "$out" | grep -E 'warning:' | grep -vcE '^\s*$' || true
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# ============================================================================
|
|
478
|
+
# JAVA PATH (REQ-004)
|
|
479
|
+
#
|
|
480
|
+
# Measured motivation: `List raw = l;` in a Java file produces ZERO diagnostics
|
|
481
|
+
# from bare javac, `[rawtypes]` under -Xlint:all, and a hard error under
|
|
482
|
+
# -Xlint:all -Werror. Gate 9's `mvn -q -DskipTests compile` suppresses the very
|
|
483
|
+
# warnings a strict gate exists to read.
|
|
484
|
+
# ============================================================================
|
|
485
|
+
|
|
486
|
+
# ---------------------------------------------------------------------------
|
|
487
|
+
# AC-004-01 — Maven arguments.
|
|
488
|
+
#
|
|
489
|
+
# `-B` (batch mode) NOT `-q`: -q suppresses non-error diagnostics, so "run mvn
|
|
490
|
+
# quietly" and "parse the warning lines" are mutually exclusive — a real
|
|
491
|
+
# self-contradiction caught in requirement round 2 (architecture CI-02).
|
|
492
|
+
# `-o` keeps a hook from reaching the network (REQ-007).
|
|
493
|
+
# `compilerArgument` is a SINGLE appended argument; -Xlint:all,-processing is
|
|
494
|
+
# one token precisely because of that (technical MC-01).
|
|
495
|
+
# ---------------------------------------------------------------------------
|
|
496
|
+
gate13_maven_args() {
|
|
497
|
+
printf '%s\n' "-B -o -DskipTests compile -Dmaven.compiler.showWarnings=true -Dmaven.compiler.showDeprecation=true -Dmaven.compiler.compilerArgument=-Xlint:all,-processing"
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
gate13_javac_args() {
|
|
501
|
+
printf '%s\n' "-Xlint:all,-processing -d"
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
gate13_java_sources() {
|
|
505
|
+
git diff --cached --name-only --diff-filter=d -- '*.java' 2>/dev/null
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
# ---------------------------------------------------------------------------
|
|
509
|
+
# AC-004-02 — is the command-line Maven injection likely to take effect?
|
|
510
|
+
#
|
|
511
|
+
# Returns 0 = reliable, 1 = unreliable. Three known failure modes, each of which
|
|
512
|
+
# silently drops the lint flags rather than failing loudly:
|
|
513
|
+
# 1. the pom already declares <compilerArgs> — a CLI single-arg append is
|
|
514
|
+
# overridden by the explicit configuration (technical MC-01)
|
|
515
|
+
# 2. maven-compiler-plugin < 3.1 — compilerArgument semantics were not stable
|
|
516
|
+
# (feasibility FC-02)
|
|
517
|
+
# 3. no plugin version declared at all — the effective version is inherited or
|
|
518
|
+
# defaulted and cannot be verified, so do not assume it works
|
|
519
|
+
#
|
|
520
|
+
# On "unreliable" the gate degrades to warn-only with a loud banner. It never
|
|
521
|
+
# fakes a PASS and never emits an unpredictable BLOCK.
|
|
522
|
+
# ---------------------------------------------------------------------------
|
|
523
|
+
# shellcheck disable=SC2016 # this function matches LITERAL ${...} in a pom, not expansions
|
|
524
|
+
gate13_maven_injection_reliable() {
|
|
525
|
+
local pom="${1:-pom.xml}" ver
|
|
526
|
+
|
|
527
|
+
[ -f "$pom" ] || return 1
|
|
528
|
+
|
|
529
|
+
if grep -q '<compilerArgs>' "$pom" 2>/dev/null; then
|
|
530
|
+
printf 'pom already declares <compilerArgs>; a command-line -Dmaven.compiler.compilerArgument may be overridden\n' >&2
|
|
531
|
+
return 1
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
# Extract the version belonging to the maven-compiler-plugin block.
|
|
535
|
+
# Walkthrough R2 MA-04: a range match to the first </plugin> could pick up a
|
|
536
|
+
# neighbouring plugin's version, and a strict numeric pattern missed
|
|
537
|
+
# `3.13.0-SNAPSHOT`. Anchor on the plugin's own artifactId, then take the FIRST
|
|
538
|
+
# <version> that follows it, and accept a qualifier suffix.
|
|
539
|
+
ver=$(awk '
|
|
540
|
+
/<artifactId>[[:space:]]*maven-compiler-plugin[[:space:]]*<\/artifactId>/ { found=1 }
|
|
541
|
+
found && /<version>/ {
|
|
542
|
+
line=$0
|
|
543
|
+
sub(/.*<version>[[:space:]]*/, "", line)
|
|
544
|
+
sub(/[[:space:]]*<\/version>.*/, "", line)
|
|
545
|
+
print line
|
|
546
|
+
exit
|
|
547
|
+
}
|
|
548
|
+
' "$pom" 2>/dev/null)
|
|
549
|
+
|
|
550
|
+
if [ -z "$ver" ]; then
|
|
551
|
+
printf 'maven-compiler-plugin version is not declared; injection cannot be verified\n' >&2
|
|
552
|
+
return 1
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
# A property placeholder cannot be resolved statically — treat as unverified
|
|
556
|
+
# rather than guessing (walkthrough R2 MA-04).
|
|
557
|
+
case "$ver" in
|
|
558
|
+
*'${'*)
|
|
559
|
+
printf 'maven-compiler-plugin version is a property reference (%s); cannot be verified statically\n' "$ver" >&2
|
|
560
|
+
return 1
|
|
561
|
+
;;
|
|
562
|
+
esac
|
|
563
|
+
|
|
564
|
+
# Drop a qualifier suffix (3.13.0-SNAPSHOT -> 3.13.0) before comparing.
|
|
565
|
+
ver=${ver%%-*}
|
|
566
|
+
|
|
567
|
+
local major minor
|
|
568
|
+
major=${ver%%.*}
|
|
569
|
+
minor=${ver#*.}
|
|
570
|
+
minor=${minor%%.*}
|
|
571
|
+
|
|
572
|
+
if [ "$major" -lt 3 ] || { [ "$major" -eq 3 ] && [ "$minor" -lt 1 ]; }; then
|
|
573
|
+
printf 'maven-compiler-plugin %s predates stable compilerArgument support (needs >= 3.1)\n' "$ver" >&2
|
|
574
|
+
return 1
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
return 0
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
# ---------------------------------------------------------------------------
|
|
581
|
+
# AC-004-03 — count javac diagnostics for a file under the strict lint set.
|
|
582
|
+
# Verified by measurement: bare javac reports nothing for a raw type, this
|
|
583
|
+
# reports [rawtypes].
|
|
584
|
+
# ---------------------------------------------------------------------------
|
|
585
|
+
gate13_javac_warning_count() {
|
|
586
|
+
local file="$1" out tmpd rc=0 budget
|
|
587
|
+
command -v javac >/dev/null 2>&1 || { printf '0\n'; return 0; }
|
|
588
|
+
|
|
589
|
+
tmpd="${TMPDIR:-/tmp}/gate13_javac_$$"
|
|
590
|
+
mkdir -p "$tmpd"
|
|
591
|
+
budget=$(gate13_timeout_budget)
|
|
592
|
+
|
|
593
|
+
# Timeout-bounded (walkthrough MC-03): a pathological file can hang javac, and
|
|
594
|
+
# a pre-commit hook must never hang with it. `timeout` may be absent on some
|
|
595
|
+
# systems, in which case we run unbounded rather than skipping the check.
|
|
596
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
597
|
+
out=$(timeout "$budget" javac -Xlint:all,-processing -d "$tmpd" "$file" 2>&1)
|
|
598
|
+
rc=$?
|
|
599
|
+
else
|
|
600
|
+
out=$(javac -Xlint:all,-processing -d "$tmpd" "$file" 2>&1)
|
|
601
|
+
rc=$?
|
|
602
|
+
fi
|
|
603
|
+
rm -rf "$tmpd"
|
|
604
|
+
|
|
605
|
+
# timeout(1) reports 124 when it fired. Returning 0 here would be
|
|
606
|
+
# indistinguishable from "compiled clean" (walkthrough round 2, CI-02), so
|
|
607
|
+
# signal incompleteness with a dedicated non-numeric marker the caller checks.
|
|
608
|
+
if [ "$rc" -eq 124 ]; then
|
|
609
|
+
printf 'TIMEOUT\n'
|
|
610
|
+
return 0
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
# javac prefixes each warning with "warning:" (localised builds may differ), so
|
|
614
|
+
# also count the "[category]" marker lines that every -Xlint diagnostic emits.
|
|
615
|
+
printf '%s\n' "$out" | grep -cE 'warning:|\[(rawtypes|unchecked|deprecation|fallthrough|finally|cast|static|try|overrides|overloads|serial|synchronization)\]' || true
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
# ---------------------------------------------------------------------------
|
|
619
|
+
# Java analysis stage. Verdict semantics mirror the C/C++ stage:
|
|
620
|
+
# 0 PASS, 1 FAIL, 2 BLOCK(untrustworthy), 3 WARN (no toolchain / degraded).
|
|
621
|
+
# ---------------------------------------------------------------------------
|
|
622
|
+
gate13_run_java_analysis() {
|
|
623
|
+
local srcs had_tool=0 degraded=0 files f cnt total=0
|
|
624
|
+
|
|
625
|
+
if gate13_is_disabled; then
|
|
626
|
+
gate13_reason "Gate 13 disabled via XP_GATE_DISABLE_13=1"
|
|
627
|
+
echo "SKIPPED - compiler strictness check (XP_GATE_DISABLE_13=1)"
|
|
628
|
+
return "$GATE13_WARN"
|
|
629
|
+
fi
|
|
630
|
+
|
|
631
|
+
srcs=$(gate13_java_sources)
|
|
632
|
+
if [ -z "$srcs" ]; then
|
|
633
|
+
return "$GATE13_PASS"
|
|
634
|
+
fi
|
|
635
|
+
|
|
636
|
+
if [ "${GATE13_FORCE_NO_JAVA_TOOLCHAIN:-}" = "1" ]; then
|
|
637
|
+
echo "WARN - no usable Java toolchain (mvn / gradle / javac); Java compiler checks NOT performed."
|
|
638
|
+
return "$GATE13_WARN"
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# ── Maven path ────────────────────────────────────────────────────────────
|
|
642
|
+
if [ -f "pom.xml" ]; then
|
|
643
|
+
if [ -x "./mvnw" ]; then
|
|
644
|
+
had_tool=1
|
|
645
|
+
elif command -v mvn >/dev/null 2>&1; then
|
|
646
|
+
had_tool=1
|
|
647
|
+
fi
|
|
648
|
+
|
|
649
|
+
if [ "$had_tool" -eq 1 ]; then
|
|
650
|
+
local mvn_bin="mvn"
|
|
651
|
+
[ -x "./mvnw" ] && mvn_bin="./mvnw"
|
|
652
|
+
|
|
653
|
+
if ! gate13_maven_injection_reliable "pom.xml" 2>/dev/null; then
|
|
654
|
+
echo " ⚠️ DEGRADED - Maven compiler-arg injection unreliable (see reasons below);"
|
|
655
|
+
echo " Java lint findings are REPORT-ONLY for this run (this is a degraded mode, not a normal warn-only run)."
|
|
656
|
+
degraded=1
|
|
657
|
+
fi
|
|
658
|
+
|
|
659
|
+
local args budget rc mvn_out mvn_log mvn_findings
|
|
660
|
+
args=$(gate13_maven_args)
|
|
661
|
+
budget=$(gate13_timeout_budget)
|
|
662
|
+
mvn_log="${TMPDIR:-/tmp}/gate13_mvn_$$.log"
|
|
663
|
+
|
|
664
|
+
# Capture BOTH streams. This is the whole point of REQ-004: `mvn compile`
|
|
665
|
+
# EXITS 0 even when javac emitted warnings, so a verdict derived from the
|
|
666
|
+
# exit code alone can never see a -Xlint finding. (Walkthrough review CI-01
|
|
667
|
+
# caught exactly that: the first implementation redirected to /dev/null and
|
|
668
|
+
# printed PASSED no matter what javac said.)
|
|
669
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
670
|
+
# shellcheck disable=SC2086
|
|
671
|
+
timeout "$budget" "$mvn_bin" $args >"$mvn_log" 2>&1
|
|
672
|
+
else
|
|
673
|
+
# shellcheck disable=SC2086
|
|
674
|
+
"$mvn_bin" $args >"$mvn_log" 2>&1
|
|
675
|
+
fi
|
|
676
|
+
rc=$?
|
|
677
|
+
mvn_out=$(cat "$mvn_log" 2>/dev/null)
|
|
678
|
+
rm -f "$mvn_log"
|
|
679
|
+
|
|
680
|
+
if [ "$rc" -eq 124 ]; then
|
|
681
|
+
gate13_classify_timeout
|
|
682
|
+
return "$GATE13_WARN"
|
|
683
|
+
fi
|
|
684
|
+
if [ "$rc" -ne 0 ]; then
|
|
685
|
+
if [ "$degraded" -eq 1 ]; then
|
|
686
|
+
# We already know the lint flags were not applied, so a build failure
|
|
687
|
+
# here says nothing about compiler strictness — it is usually missing
|
|
688
|
+
# offline dependencies. Reporting it as FAIL would blame this gate for
|
|
689
|
+
# a build-environment problem (and would be a lie: nothing was linted).
|
|
690
|
+
echo " ⚠️ Java build failed (mvn exit ${rc}) in degraded mode — not attributed to Gate 13"
|
|
691
|
+
return "$GATE13_WARN"
|
|
692
|
+
fi
|
|
693
|
+
# A non-zero exit in a run where the lint flags WERE applied means javac
|
|
694
|
+
# found something (with -Werror in a project config) or the code does not
|
|
695
|
+
# compile. Either way it is a real finding, not an environment problem.
|
|
696
|
+
echo " ❌ Java compilation failed (mvn exit ${rc})"
|
|
697
|
+
printf '%s\n' "$mvn_out" | grep -E 'ERROR|WARNING' | head -10 | sed 's/^/ /'
|
|
698
|
+
return "$GATE13_FAIL"
|
|
699
|
+
fi
|
|
700
|
+
|
|
701
|
+
if [ "$degraded" -eq 1 ]; then
|
|
702
|
+
echo " ⚠️ DEGRADED - Java compiled but lint enforcement was NOT applied; no conclusion drawn"
|
|
703
|
+
return "$GATE13_WARN"
|
|
704
|
+
fi
|
|
705
|
+
|
|
706
|
+
# Count javac's lint output. Maven prefixes it with "[WARNING] ".
|
|
707
|
+
mvn_findings=$(printf '%s\n' "$mvn_out" \
|
|
708
|
+
| grep -cE '\[WARNING\].*\[(rawtypes|unchecked|deprecation|fallthrough|finally|cast|static|try|overrides|overloads|serial|synchronization|varargs|divzero|empty|exports|module|opens|options|path|processing|removal|requires|text-blocks|this-escape)\]' \
|
|
709
|
+
| tr -d '[:space:]')
|
|
710
|
+
mvn_findings=${mvn_findings:-0}
|
|
711
|
+
|
|
712
|
+
if [ "$mvn_findings" -gt 0 ]; then
|
|
713
|
+
printf '%s\n' "$mvn_out" \
|
|
714
|
+
| grep -E '\[WARNING\].*\[[a-z-]+\]' | head -10 | sed 's/^/ /'
|
|
715
|
+
echo " ❌ ${mvn_findings} javac -Xlint finding(s)"
|
|
716
|
+
return "$GATE13_FAIL"
|
|
717
|
+
fi
|
|
718
|
+
|
|
719
|
+
echo " ✅ PASSED - Java compiled with -Xlint:all,-processing (no findings)"
|
|
720
|
+
return "$GATE13_PASS"
|
|
721
|
+
fi
|
|
722
|
+
fi
|
|
723
|
+
|
|
724
|
+
# ── Gradle path ───────────────────────────────────────────────────────────
|
|
725
|
+
if [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
|
|
726
|
+
if [ -x "./gradlew" ] || command -v gradle >/dev/null 2>&1; then
|
|
727
|
+
local g_bin="gradle"
|
|
728
|
+
[ -x "./gradlew" ] && g_bin="./gradlew"
|
|
729
|
+
local budget g_log grc g_out g_findings
|
|
730
|
+
budget=$(gate13_timeout_budget)
|
|
731
|
+
g_log="${TMPDIR:-/tmp}/gate13_gradle_$$.log"
|
|
732
|
+
|
|
733
|
+
# Same reasoning as the Maven path: capture the output, because a
|
|
734
|
+
# successful `compileJava` still carries the javac warnings.
|
|
735
|
+
# NOTE: no -q here — it suppresses exactly the diagnostics we want, the
|
|
736
|
+
# same trap that made -q wrong for Maven.
|
|
737
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
738
|
+
timeout "$budget" "$g_bin" compileJava --offline --warning-mode all >"$g_log" 2>&1
|
|
739
|
+
else
|
|
740
|
+
"$g_bin" compileJava --offline --warning-mode all >"$g_log" 2>&1
|
|
741
|
+
fi
|
|
742
|
+
grc=$?
|
|
743
|
+
g_out=$(cat "$g_log" 2>/dev/null)
|
|
744
|
+
rm -f "$g_log"
|
|
745
|
+
|
|
746
|
+
if [ "$grc" -eq 124 ]; then
|
|
747
|
+
gate13_classify_timeout
|
|
748
|
+
return "$GATE13_WARN"
|
|
749
|
+
fi
|
|
750
|
+
if [ "$grc" -ne 0 ]; then
|
|
751
|
+
echo " ❌ Java compilation failed (gradle exit ${grc})"
|
|
752
|
+
printf '%s\n' "$g_out" | grep -E 'error|warning' | head -10 | sed 's/^/ /'
|
|
753
|
+
return "$GATE13_FAIL"
|
|
754
|
+
fi
|
|
755
|
+
|
|
756
|
+
g_findings=$(printf '%s\n' "$g_out" \
|
|
757
|
+
| grep -cE '\[(rawtypes|unchecked|deprecation|fallthrough|cast|static|overrides|serial|synchronization)\]' \
|
|
758
|
+
| tr -d '[:space:]')
|
|
759
|
+
g_findings=${g_findings:-0}
|
|
760
|
+
if [ "$g_findings" -gt 0 ]; then
|
|
761
|
+
printf '%s\n' "$g_out" | grep -E 'warning:' | head -10 | sed 's/^/ /'
|
|
762
|
+
echo " ❌ ${g_findings} javac lint finding(s) (gradle compileJava)"
|
|
763
|
+
return "$GATE13_FAIL"
|
|
764
|
+
fi
|
|
765
|
+
echo " ✅ PASSED - Java compiled (gradle compileJava, no findings)"
|
|
766
|
+
return "$GATE13_PASS"
|
|
767
|
+
fi
|
|
768
|
+
fi
|
|
769
|
+
|
|
770
|
+
# ── Bare javac path ───────────────────────────────────────────────────────
|
|
771
|
+
if command -v javac >/dev/null 2>&1; then
|
|
772
|
+
local blocking=0 timed_out=0
|
|
773
|
+
while IFS= read -r f; do
|
|
774
|
+
[ -n "$f" ] || continue
|
|
775
|
+
cnt=$(gate13_javac_warning_count "$f")
|
|
776
|
+
# A timed-out file was NOT checked. Treating it as 0 warnings would report
|
|
777
|
+
# a pass for code nobody looked at (walkthrough R2 CI-02).
|
|
778
|
+
if [ "$cnt" = "TIMEOUT" ]; then
|
|
779
|
+
gate13_classify_timeout
|
|
780
|
+
echo " ⚠️ ${f}: javac timed out — not analysed"
|
|
781
|
+
timed_out=$((timed_out + 1))
|
|
782
|
+
continue
|
|
783
|
+
fi
|
|
784
|
+
cnt=${cnt:-0}
|
|
785
|
+
if [ "$cnt" -gt 0 ]; then
|
|
786
|
+
total=$((total + cnt))
|
|
787
|
+
echo " ❌ ${f}: ${cnt} javac -Xlint finding(s)"
|
|
788
|
+
blocking=$((blocking + 1))
|
|
789
|
+
fi
|
|
790
|
+
done <<EOF
|
|
791
|
+
$srcs
|
|
792
|
+
EOF
|
|
793
|
+
if [ "$blocking" -gt 0 ]; then
|
|
794
|
+
echo " ❌ BLOCKED - ${blocking} Java file(s) with -Xlint findings (${total} total)"
|
|
795
|
+
return "$GATE13_FAIL"
|
|
796
|
+
fi
|
|
797
|
+
if [ "$timed_out" -gt 0 ]; then
|
|
798
|
+
echo " ⚠️ WARN - ${timed_out} Java file(s) were not analysed (timeout); coverage incomplete"
|
|
799
|
+
return "$GATE13_WARN"
|
|
800
|
+
fi
|
|
801
|
+
echo " ✅ PASSED - Java compiler strictness (javac -Xlint:all,-processing)"
|
|
802
|
+
return "$GATE13_PASS"
|
|
803
|
+
fi
|
|
804
|
+
|
|
805
|
+
echo "WARN - no usable Java toolchain (mvn / gradle / javac); Java compiler checks NOT performed."
|
|
806
|
+
return "$GATE13_WARN"
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
# ============================================================================
|
|
810
|
+
# `-fanalyzer` PATH (REQ-005) — C files only, opt-in.
|
|
811
|
+
#
|
|
812
|
+
# Path-sensitive analysis that finds use-after-free / double-free / NULL
|
|
813
|
+
# dereference no warning flag reaches. OFF by default for three measured reasons:
|
|
814
|
+
# * speed — seconds per translation unit, not milliseconds
|
|
815
|
+
# * portability— GCC-only; clang errors on the flag
|
|
816
|
+
# * semantics — it emits NOTHING under -fsyntax-only (measured), because the
|
|
817
|
+
# analysis hangs off real optimisation passes. It therefore has
|
|
818
|
+
# to compile for real, which is why artifacts go to a temp dir
|
|
819
|
+
# with -c (never a linked binary, never the work tree).
|
|
820
|
+
# Requirement round 2 (feasibility FC-04) restricted it to .c files: C++ TUs
|
|
821
|
+
# through -fanalyzer are dramatically slower and noisier.
|
|
822
|
+
# ============================================================================
|
|
823
|
+
|
|
824
|
+
gate13_analyzer_enabled() {
|
|
825
|
+
[ "${XP_GATE_COMPILER_ANALYZER:-}" = "1" ]
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
# Print the staged C (not C++) files eligible for analysis.
|
|
829
|
+
gate13_analyzer_files() {
|
|
830
|
+
git diff --cached --name-only --diff-filter=d -- '*.c' 2>/dev/null
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
# Can this compiler do -fanalyzer at all?
|
|
834
|
+
gate13_analyzer_supported() {
|
|
835
|
+
local cc="$1" out tu
|
|
836
|
+
[ -n "$cc" ] || return 1
|
|
837
|
+
command -v "$cc" >/dev/null 2>&1 || return 1
|
|
838
|
+
|
|
839
|
+
tu="${GATE13_ANALYZER_TU:-}"
|
|
840
|
+
if [ -z "$tu" ] || [ ! -f "$tu" ]; then
|
|
841
|
+
tu="${TMPDIR:-/tmp}/gate13_analyzer_probe_$$.c"
|
|
842
|
+
printf 'int main(void) { return 0; }\n' > "$tu"
|
|
843
|
+
GATE13_ANALYZER_TU="$tu"
|
|
844
|
+
fi
|
|
845
|
+
|
|
846
|
+
out=$("$cc" -fanalyzer -fsyntax-only "$tu" 2>&1) && return 0
|
|
847
|
+
case "$out" in
|
|
848
|
+
*"unknown argument"*|*"unrecognized command-line option"*|*"unknown option"*|*"not supported"*)
|
|
849
|
+
return 1
|
|
850
|
+
;;
|
|
851
|
+
esac
|
|
852
|
+
# It failed for another reason (e.g. no C frontend): treat as unsupported
|
|
853
|
+
# rather than silently claiming the analysis ran.
|
|
854
|
+
return 1
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
gate13_run_analyzer() {
|
|
858
|
+
local cc
|
|
859
|
+
local files=""
|
|
860
|
+
local f cnt total=0 blocking=0
|
|
861
|
+
local workdir
|
|
862
|
+
|
|
863
|
+
gate13_analyzer_enabled || return 0
|
|
864
|
+
|
|
865
|
+
cc=$(gate13_select_cc)
|
|
866
|
+
[ -n "$cc" ] || {
|
|
867
|
+
echo " ⚠️ -fanalyzer requested but no C compiler is usable; analyzer stage skipped"
|
|
868
|
+
return 0
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if ! gate13_analyzer_supported "$cc"; then
|
|
872
|
+
echo " ℹ️ $(gate13_compiler_id "$cc") has no -fanalyzer support; analyzer stage skipped"
|
|
873
|
+
return 0
|
|
874
|
+
fi
|
|
875
|
+
|
|
876
|
+
files=$(gate13_analyzer_files)
|
|
877
|
+
if [ -z "$files" ]; then
|
|
878
|
+
echo " ℹ️ -fanalyzer: no staged .c files"
|
|
879
|
+
return 0
|
|
880
|
+
fi
|
|
881
|
+
|
|
882
|
+
workdir="${TMPDIR:-/tmp}/gate13_analyzer_$$"
|
|
883
|
+
mkdir -p "$workdir"
|
|
884
|
+
|
|
885
|
+
while IFS= read -r f; do
|
|
886
|
+
[ -n "$f" ] || continue
|
|
887
|
+
cnt=$("$cc" -fanalyzer -c -o "$workdir/$(basename -- "$f").o" "$f" 2>&1 \
|
|
888
|
+
| grep -cE 'warning:|\[-Wanalyzer' || true)
|
|
889
|
+
cnt=${cnt:-0}
|
|
890
|
+
if [ "$cnt" -gt 0 ]; then
|
|
891
|
+
total=$((total + cnt))
|
|
892
|
+
echo " ⚠️ ${f}: ${cnt} -fanalyzer finding(s)"
|
|
893
|
+
blocking=$((blocking + 1))
|
|
894
|
+
fi
|
|
895
|
+
done <<EOF
|
|
896
|
+
$files
|
|
897
|
+
EOF
|
|
898
|
+
|
|
899
|
+
rm -rf "$workdir"
|
|
900
|
+
|
|
901
|
+
if [ "$total" -gt 0 ]; then
|
|
902
|
+
echo " ℹ️ -fanalyzer: ${total} finding(s) across ${blocking} file(s) (advisory, opt-in stage)"
|
|
903
|
+
else
|
|
904
|
+
echo " ✅ -fanalyzer: clean"
|
|
905
|
+
fi
|
|
906
|
+
return 0
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
# ---------------------------------------------------------------------------
|
|
910
|
+
# AC-003-01/02/03/05 — the C/C++ analysis stage.
|
|
911
|
+
#
|
|
912
|
+
# Policy:
|
|
913
|
+
# added file -> -Werror: ANY warning in the active tier blocks
|
|
914
|
+
# modified file -> compare count to baseline; no baseline => report-only
|
|
915
|
+
# untouched file -> never compiled
|
|
916
|
+
# warn-only tier -> nothing blocks; findings surface as WARN
|
|
917
|
+
# ---------------------------------------------------------------------------
|
|
918
|
+
gate13_run_cpp_analysis() {
|
|
919
|
+
local cxx budget tier flags
|
|
920
|
+
local added_list="" modified_list=""
|
|
921
|
+
local blocking=0 findings=0 baseline_seen=0
|
|
922
|
+
|
|
923
|
+
if gate13_is_disabled; then
|
|
924
|
+
gate13_reason "Gate 13 disabled via XP_GATE_DISABLE_13=1"
|
|
925
|
+
echo "SKIPPED - compiler strictness check (XP_GATE_DISABLE_13=1)"
|
|
926
|
+
return "$GATE13_WARN"
|
|
927
|
+
fi
|
|
928
|
+
|
|
929
|
+
budget=$(gate13_timeout_budget)
|
|
930
|
+
cxx=$(gate13_select_cxx)
|
|
931
|
+
local sel_rc=$?
|
|
932
|
+
if [ "$sel_rc" -ne 0 ] || [ -z "$cxx" ]; then
|
|
933
|
+
echo "WARN - no usable C/C++ compiler found; compiler checks NOT performed."
|
|
934
|
+
echo " Install one of: g++ / clang++ (Debian: build-essential, macOS: xcode-select --install)"
|
|
935
|
+
return "$GATE13_WARN"
|
|
936
|
+
fi
|
|
937
|
+
|
|
938
|
+
if gate13_is_cross_compiling "$cxx"; then
|
|
939
|
+
gate13_reason "cross-compilation detected (compiler: ${cxx}, CROSS_COMPILE=${CROSS_COMPILE:-<empty>}); skipping host-side compile checks"
|
|
940
|
+
echo "WARN - cross-compilation detected: ${cxx}"
|
|
941
|
+
echo " Target-architecture code is not compiled with a host compiler; compiler checks NOT performed."
|
|
942
|
+
return "$GATE13_WARN"
|
|
943
|
+
fi
|
|
944
|
+
|
|
945
|
+
tier=$(gate13_resolve_tier)
|
|
946
|
+
flags=$(gate13_cpp_flags "$tier" cpp)
|
|
947
|
+
local probed
|
|
948
|
+
# shellcheck disable=SC2086 # $flags is a deliberately word-split flag list
|
|
949
|
+
probed=$(gate13_probe_flags "$cxx" cpp $flags)
|
|
950
|
+
[ -n "$probed" ] && flags="$probed"
|
|
951
|
+
|
|
952
|
+
echo "compiler: ${cxx} ($(gate13_compiler_id "$cxx"))"
|
|
953
|
+
echo "strictness: ${tier} | flags: ${flags} | budget: ${budget}s"
|
|
954
|
+
|
|
955
|
+
local classified
|
|
956
|
+
classified=$(gate13_classify_changed)
|
|
957
|
+
if [ -z "$classified" ]; then
|
|
958
|
+
echo " ℹ️ No staged C/C++ source files — nothing to compile"
|
|
959
|
+
return "$GATE13_PASS"
|
|
960
|
+
fi
|
|
961
|
+
|
|
962
|
+
if ! gate13_baseline_exists; then
|
|
963
|
+
echo " ⚠️ baseline missing — run 'xp-gate compiler-baseline create' to enable enforcement on modified files"
|
|
964
|
+
echo " (new files are still -Werror; modified files are report-only until then)"
|
|
965
|
+
fi
|
|
966
|
+
|
|
967
|
+
local f kind cnt base
|
|
968
|
+
while IFS=$'\t' read -r kind f; do
|
|
969
|
+
[ -n "$f" ] || continue
|
|
970
|
+
|
|
971
|
+
if [ "$kind" = "added" ]; then
|
|
972
|
+
# AC-003-01: an added file's baseline count is inherently zero, so it is
|
|
973
|
+
# enforced even without a baseline file.
|
|
974
|
+
cnt=$(gate13_count_warnings "$cxx" "$flags" "$f")
|
|
975
|
+
cnt=${cnt:-0}
|
|
976
|
+
if [ "$cnt" -gt 0 ]; then
|
|
977
|
+
findings=$((findings + cnt))
|
|
978
|
+
if [ "$tier" = "warn-only" ]; then
|
|
979
|
+
echo " ⚠️ ${f}: ${cnt} warning(s) (warn-only tier, not blocking)"
|
|
980
|
+
else
|
|
981
|
+
echo " ❌ ${f}: ${cnt} warning(s) in a NEW file"
|
|
982
|
+
blocking=$((blocking + 1))
|
|
983
|
+
fi
|
|
984
|
+
fi
|
|
985
|
+
else
|
|
986
|
+
# AC-003-02/03: modified file.
|
|
987
|
+
base=$(gate13_baseline_count "$f")
|
|
988
|
+
if [ -z "$base" ]; then
|
|
989
|
+
baseline_seen=1
|
|
990
|
+
cnt=$(gate13_count_warnings "$cxx" "$flags" "$f")
|
|
991
|
+
echo " ℹ️ ${f}: ${cnt:-0} warning(s) (no baseline entry, report-only)"
|
|
992
|
+
continue
|
|
993
|
+
fi
|
|
994
|
+
cnt=$(gate13_count_warnings "$cxx" "$flags" "$f")
|
|
995
|
+
cnt=${cnt:-0}
|
|
996
|
+
if [ "$cnt" -gt "$base" ]; then
|
|
997
|
+
findings=$((findings + cnt))
|
|
998
|
+
if [ "$tier" = "warn-only" ]; then
|
|
999
|
+
echo " ⚠️ ${f}: ${base} -> ${cnt} warning(s) (warn-only tier, not blocking)"
|
|
1000
|
+
else
|
|
1001
|
+
echo " ❌ ${f}: warning count rose ${base} -> ${cnt} (baseline exceeded)"
|
|
1002
|
+
blocking=$((blocking + 1))
|
|
1003
|
+
fi
|
|
1004
|
+
fi
|
|
1005
|
+
fi
|
|
1006
|
+
done <<EOF
|
|
1007
|
+
$classified
|
|
1008
|
+
EOF
|
|
1009
|
+
|
|
1010
|
+
if [ "$tier" = "warn-only" ]; then
|
|
1011
|
+
if [ "$findings" -gt 0 ]; then
|
|
1012
|
+
echo " ⚠️ WARN - ${findings} C/C++ compiler finding(s) (warn-only tier)"
|
|
1013
|
+
fi
|
|
1014
|
+
return "$GATE13_WARN"
|
|
1015
|
+
fi
|
|
1016
|
+
|
|
1017
|
+
if [ "$blocking" -gt 0 ]; then
|
|
1018
|
+
echo " ❌ BLOCKED - ${blocking} file(s) with compiler findings (${findings} warning(s) total)"
|
|
1019
|
+
echo " Fix the warnings, or (for pre-existing code) record a baseline:"
|
|
1020
|
+
echo " xp-gate compiler-baseline create"
|
|
1021
|
+
return "$GATE13_FAIL"
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
echo " ✅ PASSED - C/C++ compiler strictness (${tier} tier)"
|
|
1025
|
+
|
|
1026
|
+
# NOTE: gate13_run_analyzer is deliberately NOT called here. pre-commit drives
|
|
1027
|
+
# the three stages separately and reduces their verdicts itself; calling it in
|
|
1028
|
+
# both places ran -fanalyzer twice (walkthrough MC-01), doubling the slowest
|
|
1029
|
+
# stage for no benefit.
|
|
1030
|
+
return "$GATE13_PASS"
|
|
1031
|
+
}
|