# Dead Guard Detection

Homeboy audit flags `function_exists('…')`, `class_exists('…')`, and `defined('…')` guards whose
checked symbol is guaranteed to exist at runtime. Such guards are reachable-but-dead: the `else`
branch can never fire, the code becomes harder to read, and refactors keep carrying forward stale
defensive scaffolding.

## How the detector decides a symbol is guaranteed

A symbol is considered guaranteed when any of the following hold:

- The plugin declares `Requires at least: X.Y` in its main-file header and the symbol shipped in
  Core at or before that version (table covers common symbols such as `WP_Ability` @ 6.9,
  `wp_generate_uuid4` @ 4.7, `wp_timezone` @ 5.3).
- The plugin's main file contains an **unconditional** `require` / `require_once` of a well-known
  vendor bootstrap (e.g. `vendor/woocommerce/action-scheduler/action-scheduler.php`). Requires
  inside an `if ( ! class_exists(…) ) { … }` block are ignored.
- `composer.json` lists a known package under `require` or `require-dev` whose symbols the detector
  recognizes (e.g. `woocommerce/action-scheduler`).

Both direct and negated guards are reported:

```php
if ( ! class_exists( 'WP_Ability' ) ) { return; } // flagged
if ( function_exists( 'as_schedule_single_action' ) ) { … }  // flagged when AS is bootstrapped
```

## Finding output

- `convention`: `dead_guard`
- `kind`: `dead_guard`
- `severity`: `warning`

Dead-guard findings participate in baseline comparisons like any other audit finding.

## Context comments

Extensions can configure `audit.detector_rules.dead_guard_context_comment_patterns` with regexes that
mark a specific guard as intentionally dual-context. Core only matches the configured regexes against
comments near the guard; extensions own the wording and domain conventions.

The comment context is the guard line plus the guarded `if` arm when it has braces, optionally prefixed
by the immediately preceding comment block. Non-comment code and string literals are not matched.

## Extending the symbol table

The WP-core symbol table lives in `src/core/code_audit/requirements.rs`. Add new rows as
`(symbol_name, introduced_in_encoded_version, kind)` — `kind` is `'f'` for functions, `'c'` for
classes, `'k'` for constants. Version is encoded as `major * 100 + minor` (e.g. 6.9 → 609).

Vendor packages are seeded via `seed_vendor_symbols_from_path` (bootstrap-require match) and
`apply_composer_requires` (composer.json match). Add new packages there.
