Skip to content

fix(audits): exempt #[cfg(test)]-gated items from code-unwrap - #77

Merged
brettdavies merged 1 commit into
devfrom
feat/p6-code-unwrap-cfg-test
Jun 2, 2026
Merged

fix(audits): exempt #[cfg(test)]-gated items from code-unwrap#77
brettdavies merged 1 commit into
devfrom
feat/p6-code-unwrap-cfg-test

Conversation

@brettdavies

Copy link
Copy Markdown
Owner

Summary

code-unwrap flagged every .unwrap() inside inline #[cfg(test)] mod tests { ... }, gated helper functions, and gated impl blocks even though --include-tests was off by default. The pre-existing filter only excluded the tests/ directory and *_test.rs filenames, so the canonical Rust inline-test-module convention was mis-classified as production code and inflated false-positive counts on every Rust project that audits its own crate.

This PR replaces the flat $RECV.unwrap() ast-grep pattern with a tree-sitter walk that tracks whether the current node sits inside a #[cfg(test)]-gated item. Attributes are sibling nodes in tree-sitter-rust (not children), so the walk propagates a one-shot "next sibling is cfg-test-gated" flag that consumes on the very next sibling regardless of kind: a #[cfg(test)] use foo; correctly consumes the flag without gating subsequent items.

The cfg-args parser recognises a bare test predicate at any nesting under any(...) / all(...) / not(...) while rejecting feature = "test" (string literals are skipped), testing_only / test_helper (full-identifier match only), and unrelated predicates like cfg(unix). Threading project.include_tests into the audit preserves the existing --include-tests opt-in for callers that do want to audit test code.

Verified on xurl-rs (the motivating repo): the audit drops from fail with ~50 evidence lines to pass.

Changelog

Fixed

  • Exempt .unwrap() calls inside #[cfg(test)]-gated items (inline mod tests, gated helper functions, gated impl blocks) from code-unwrap by default; --include-tests restores the prior behaviour.

Type of Change

  • fix: Bug fix (non-breaking change which fixes an issue)

Related Issues/Stories

  • Plan: docs/plans/2026-06-02-001-feat-anc-100-percent-compliance-plan.md U1 (xurl-rs side)

Testing

  • Unit tests added/updated
  • All tests passing

Test Summary:

  • 13 new unit tests in src/audits/source/rust/unwrap.rs covering: inline mod tests exemption, nested gating, #[cfg(test)] use not gating later items, cfg(any(test, feature = "x")), cfg(all(test, unix)), feature = "test" not gating, test_helper / cfg(unix) not gating, gated impl blocks, and --include-tests regression.
  • Full crate suite: cargo test → 810 passing, 2 ignored.
  • cargo clippy --all-targets -- -D warnings clean.
  • Manual verification against xurl-rs: code-unwrap flips from fail (~50 evidence lines) to pass.

Files Modified

Modified:

  • src/audits/source/rust/unwrap.rs — tree-sitter walk, cfg-args parser, expanded test suite.

Breaking Changes

  • No breaking changes

The audit becomes strictly more permissive by default; callers that depend on the old behaviour can pass --include-tests.

`code-unwrap` flagged every `.unwrap()` inside inline `#[cfg(test)] mod
tests { ... }`, gated helper functions, and gated `impl` blocks even
though `--include-tests` was off by default. Only the `tests/` directory
and `*_test.rs` filenames were excluded; the canonical inline-test-module
convention was mis-classified as production code.

Replace the flat `$RECV.unwrap()` ast-grep pattern with a tree-sitter
walk that tracks whether the current node sits inside a
`#[cfg(test)]`-gated item. Attributes are sibling nodes in
tree-sitter-rust, not children, so the walk propagates a one-shot
"next sibling is cfg-test-gated" flag that consumes on the very next
sibling regardless of kind — a `#[cfg(test)] use foo;` correctly
consumes the flag without gating subsequent items.

The cfg-args parser recognises a bare `test` predicate at any nesting
under `any(...)` / `all(...)` / `not(...)` while rejecting
`feature = "test"` (string literals are skipped), `testing_only` /
`test_helper` (full-identifier match only), and unrelated predicates
like `cfg(unix)`. Threading `project.include_tests` into the audit
preserves the existing `--include-tests` opt-in for callers that do
want to audit test code.
@brettdavies
brettdavies merged commit 817d6fa into dev Jun 2, 2026
8 checks passed
@brettdavies
brettdavies deleted the feat/p6-code-unwrap-cfg-test branch June 2, 2026 19:36
brettdavies added a commit that referenced this pull request Jun 3, 2026
…llow-up plans

Adds five plans surfaced by the post-merge adversarial review of PRs #73, #76, #77, and #79:

- 2026-06-03-001 (umbrella, P1): structure-over-vocabulary audit philosophy.
  Spec is canonical; closed-vocabulary lookups (COMMON_VERBS, STANDARD_VERBS,
  DISCRIMINANT_FIELD_NAMES, SUCCESS_VALUE_STRINGS) are failed quality proxies;
  per-PR redesigns of p6-consistent-naming and p2-must-json-errors land under
  this plan.
- 2026-06-03-002 (P1): PR #73 follow-up. Probe-failure now Warns instead of
  silent Pass, leaf non-verb requires a successful probe, drop emit from
  COMMON_VERBS, add 4 negative-case tests.
- 2026-06-03-003 (P1): PR #76 follow-up. Scorecard transparency for
  .anc.toml domain_verbs (using_domain_verbs flag + domain_match_count +
  evidence string showing built-in vs domain ratio), trim 7 platform-specific
  verbs out of built-in STANDARD_VERBS, document the social-CLI .anc.toml
  pattern.
- 2026-06-03-004 (P0): PR #77 follow-up. cfg(not(test)) is treated identically
  to cfg(test) in the new cfg-args parser, silently exempting production-only
  code from code-unwrap. Thread polarity through the recursive call site,
  add 8 negative-case tests, correct an inner-attribute comment.
- 2026-06-03-005 (P1): PR #79 follow-up. Close three role-based-validator
  bypasses: numeric discriminant on info-coded values, success synonyms outside
  the 8-string closed set, and nested-recursion harvesting roles from a
  success-coded outer envelope. Add 6 adversarial tests.

Plans 002, 003, and 005 are tactical stopgaps that stop active misclassification
while plan 001 owns the long-term redesign. Plan 004 is a correctness bug fix
and ships independently.
brettdavies added a commit that referenced this pull request Jun 4, 2026
…tests (#80)

## Summary

Closes a P0 correctness regression in `code-unwrap` surfaced by the
adversarial review of merged PR #77.

PR #77 (commit 817d6fa) introduced a tree-sitter walk that exempts
`.unwrap()` calls inside `#[cfg(test)]`-gated items. The cfg-args
parser, however, treats `not(...)` identically to `any(...)` and
`all(...)`: it descends into the inner argument list without polarity
tracking and returns `true` whenever the bare identifier `test` appears
at any depth. The effect was that `#[cfg(not(test))]` on production-only
code silently exempted that code from the audit, a regression confirmed
both by reading the parser and by an injected adversarial test that
panicked under the buggy logic.

This PR threads a `negated: bool` parameter through
`cfg_args_contain_test`. The bare-test branch returns `true` only at
even parity (`!negated`); at odd parity (under a `not(...)`) the
predicate means production code and the walker keeps scanning siblings
in case another predicate resolves to test-gated. The recursive call for
`not(...)` flips polarity; `any(...)` and `all(...)` preserve it.
Double-negation (`not(not(test))`) correctly restores even parity.

Also corrects the inner-attribute comment block (which claimed sticky
propagation across all siblings while the code actually does one-shot
reset) and pins the intentional asymmetry with a named test.

Per plan #4, the diff is surgical: only the parser function, the
inner-attribute comment, and the test module are touched.

## Changelog

### Fixed

- `code-unwrap`: `#[cfg(not(test))]` no longer silently exempts
production-only code from the audit. The cfg-args parser now tracks
polarity through `not(...)` wrappers; `not(test)`, `any(not(test),
unix)`, and `all(not(test), feature = "x")` correctly classify as
production. Polarity-pinning tests cover the double-negation case
(`not(not(test))`), the `not(any(test))` shape, `cfg_attr(test, …)`
non-gating, and the inner-attribute one-shot reset asymmetry.

## Type of Change

- [x] `fix`: Bug fix (non-breaking change which fixes an issue)

## Related Issues/Stories

- Plan:
`docs/plans/2026-06-03-004-fix-pr77-code-unwrap-cfg-not-test-polarity-plan.md`
- Origin: adversarial review of merged PR #77 (commit 817d6fa)
- Umbrella plan (audit philosophy, supersedes plans #2 and #5):
`docs/plans/2026-06-03-001-refactor-audit-philosophy-structure-over-vocabulary-plan.md`

## Testing

- [x] Unit tests added/updated
- [x] All tests passing

**Test Summary:**

- `cargo test`: 846 passed, 2 ignored (8 suites). 9 new unit tests in
`src/audits/source/rust/unwrap.rs`:
`cfg_not_test_does_not_exempt_production_unwrap`,
`cfg_any_not_test_unix_does_not_exempt`,
`cfg_all_not_test_feature_does_not_exempt`,
`cfg_any_test_or_not_feature_still_exempts`,
`cfg_attr_test_does_not_exempt`,
`inner_attribute_one_shot_consumes_on_use_not_fn`,
`mod_tests_without_gate_does_not_exempt`,
`cfg_not_not_test_does_exempt`, `cfg_not_any_test_does_not_exempt`.
- `cargo clippy --all-targets -- -Dwarnings`: clean.
- Dogfood (`anc audit .`): `code-unwrap` flags one production unwrap at
`src/audits/behavioral/json_errors.rs:192`
(`trimmed.chars().next().unwrap()`). The unwrap has no cfg gate; it
surfaces because PR #79 added it and the audit wasn't re-dogfooded after
that merge. Verified identical behavior on pre-fix `dev` via `git stash`
+ rebuild, ruling out a regression from this PR. Owned by plan #5 (PR
#79 follow-up) or addressable as a one-line fix in a separate PR.
- Dogfood (`anc audit ~/dev/xurl-rs`): `code-unwrap` continues to
`pass`. xurl-rs has zero `#[cfg(not(test))]` items, so the polarity fix
is a no-op against that target.

## Files Modified

**Modified:**

- `src/audits/source/rust/unwrap.rs`: thread `negated: bool` through
`cfg_args_contain_test`; flip polarity on `not(...)` recursion (preserve
on `any`/`all`); rewrite the inner-attribute comment block to match
one-shot reset semantics and name the pinning test; add 9 negative-case
unit tests.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

The audit becomes strictly more accurate. Callers that previously relied
on `#[cfg(not(test))]` exempting production code from `code-unwrap` were
doing so accidentally, since that path was an unintended consequence of
PR #77's missing polarity tracking.

## Deployment Notes

- [x] No special deployment steps required

## Checklist

- [x] Code follows project conventions and style guidelines
- [x] Commit messages follow Conventional Commits
- [x] Self-review of code completed
- [x] Tests added/updated and passing
- [x] No new warnings or errors introduced
- [x] Changes are backward compatible
brettdavies added a commit that referenced this pull request Jun 4, 2026
…ue (#82)

## Summary

Addresses the `json_errors.rs:192` finding from PR #80's dogfood run:
the `.unwrap()` on `trimmed.chars().next()` inside `is_type_id_value`
was provably safe (an `is_empty()` guard ten lines earlier guarantees at
least one char), but the `code-unwrap` source audit pattern-matches on
`.unwrap()` call expressions and surfaced it during self-dogfood.

Replaces the `.unwrap()` with a `let-else` that returns `false`,
consistent with the function's "return false on any invalid input"
pattern. Adds a comprehensive unit test
(`is_type_id_value_validates_json_shapes`) covering all five non-string
JSON value kinds (Number, Bool, Null, Array, Object), three valid
identifier shapes (kebab-case, SCREAMING_SNAKE, snake_case), and three
invalid string shapes (whitespace, period-separated, leading digit).

## Changelog

### Fixed

- Fix `code-unwrap` self-dogfood finding at `json_errors.rs:192`:
replace `.unwrap()` with a `let-else` returning `false` in
`is_type_id_value`.

## Type of Change

- [x] `fix`: Bug fix (non-breaking change which fixes an issue)

## Related Issues/Stories

- PR #80 (dogfood surfaced `json_errors.rs:192` as the one remaining
`code-unwrap` finding)
- PR #77 (introduced the `code-unwrap` audit)
- PR #79 (introduced the `.unwrap()` that this PR removes)

## Testing

- [x] Unit tests added/updated
- [x] All tests passing

**Test Summary:**

- `cargo test`: 848 passed, 2 ignored (8 suites). +1 new test
`is_type_id_value_validates_json_shapes` in
`src/audits/behavioral/json_errors.rs`.
- `cargo clippy --all-targets -- -Dwarnings`: clean.
- `cargo fmt --check`: clean.

## Files Modified

**Modified:**

- `src/audits/behavioral/json_errors.rs`: replace `.unwrap()` with
`let-else` at line 192; add `is_type_id_value_validates_json_shapes`
unit test.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

## Deployment Notes

- [x] No special deployment steps required

## Checklist

- [x] Code follows project conventions and style guidelines
- [x] Commit messages follow Conventional Commits
- [x] Self-review of code completed
- [x] Tests added/updated and passing
- [x] No new warnings or errors introduced
- [x] Changes are backward compatible
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant