feat(cfg): surface CDG soundness skips at warn, not just debug (#2195)

skippedUnsoundFunctions (a function whose EXIT is not reverse-reachable from
all blocks, so control dependence is withheld) was only reported inside the
per-language logger.debug stats line — while the taint/RD coverage-gap and
cap-drop counts surface unconditionally at warn. A language that systematically
trapped EXIT (an unmodeled non-terminating / multi-terminal shape the
synthetic-escape pass can't bridge) would silently lose all CDG. Add a parallel
unconditional warn (R8) alongside the R4 taint-gap warn. Observability only —
no graph change; emit-layer skip counting stays covered by cfg-emit's
skippedUnsoundFunctions test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergo Magyar 2026-06-14 19:21:08 +00:00
parent 194c36befc
commit a805e8fa6f

View file

@ -992,6 +992,20 @@ export function runScopeResolution(
: ''),
);
}
// R8 (#2195): CDG soundness skips surface UNCONDITIONALLY (parity with the
// taint/RD gap warns) — not buried in the logger.debug stats line above. A
// function whose EXIT is not reverse-reachable from every block gets NO
// control dependence (an unmodeled non-terminating / multi-terminal CFG
// shape the synthetic-escape pass could not bridge). Withholding CDG
// silently would let a language's control dependence erode unnoticed; CFG
// and REACHING_DEF do not depend on post-dominance and are unaffected.
if (cdgSkippedUnsound > 0) {
logger.warn(
`[cfg] lang=${provider.language}: ${cdgSkippedUnsound} function(s) had control ` +
`dependence skipped (EXIT not reverse-reachable from all blocks); ` +
`CFG and REACHING_DEF are unaffected`,
);
}
// R4: taint coverage gaps and cap drops surface UNCONDITIONALLY (never
// logger.debug, never input.onWarn) at the per-language aggregate, with
// counts and up to 5 example functions. Per-function warns above cover