Skip to content

/dead-code-analyze

Workflow Diagram

Extract, triage, and verify code items for dead code with iterative re-scanning to fixed-point.

flowchart TD
    Start([Start: Scope Selected]) --> Extract[Extract Code Items]

    Extract --> ParseDiff[Parse Added Lines]
    ParseDiff --> RecordItems[Record Type/Name/Location]
    RecordItems --> GroupPairs[Group Symmetric Pairs]

    GroupPairs --> Triage[Present All Items to User]
    Triage --> Proceed{User Approves?}
    Proceed -->|No| Abort([Abort])
    Proceed -->|Yes| Verify

    Verify[Generate Dead Code Claim] --> Search[Search Entire Codebase]
    Search --> DirectCalls[Check Direct Callers]
    DirectCalls --> Exports[Check Exports]
    Exports --> Dynamic[Check Dynamic Invocation]

    Dynamic --> Evidence{Usage Evidence?}
    Evidence -->|Zero Callers| Dead[Mark DEAD]
    Evidence -->|Self-Call Only| Dead
    Evidence -->|Write-Only| WriteOnly[Mark WRITE-ONLY DEAD]
    Evidence -->|Dead Callers Only| TransCheck[Check Transitive]
    Evidence -->|Test-Only| AskUser{Ask User: Keep?}
    Evidence -->|Live Callers| Alive[Mark ALIVE]

    WriteOnly --> MoreItems
    TransCheck --> TransLoop{All Callers Dead?}
    TransLoop -->|Yes| TransDead[Mark TRANSITIVE DEAD]
    TransLoop -->|No| Alive
    AskUser -->|Keep| Alive
    AskUser -->|Remove| Dead

    Dead --> MoreItems{More Items?}
    TransDead --> MoreItems
    Alive --> MoreItems
    MoreItems -->|Yes| Verify
    MoreItems -->|No| SymPairs

    SymPairs[Symmetric Pair Analysis] --> PairCheck{Pair Status?}
    PairCheck -->|All Dead| GroupDead[Mark Group Dead]
    PairCheck -->|Mixed| FlagAsymmetry[Flag for Review]
    PairCheck -->|All Alive| GroupAlive[Mark Group Alive]

    GroupDead --> Rescan
    FlagAsymmetry --> Rescan
    GroupAlive --> Rescan

    Rescan{New Dead Code Found?}
    Rescan -->|Yes| ReExtract[Re-extract Remaining Items]
    ReExtract --> Verify
    Rescan -->|No| FixedPoint

    FixedPoint[Fixed-Point Reached] --> OptVerify{Experimental Verify?}
    OptVerify -->|Yes| RemoveTest[/Remove and Run Tests/]
    RemoveTest --> TestResult{Tests Pass?}
    TestResult -->|Yes| Confirmed[Confirmed Dead]
    TestResult -->|No| NotDead[Code Was Used]
    OptVerify -->|No| Done

    Confirmed --> Done([Output Verdicts + Evidence])
    NotDead --> Done

    style Start fill:#2196F3,color:#fff
    style Extract fill:#2196F3,color:#fff
    style ParseDiff fill:#2196F3,color:#fff
    style RecordItems fill:#2196F3,color:#fff
    style GroupPairs fill:#2196F3,color:#fff
    style Triage fill:#2196F3,color:#fff
    style Proceed fill:#FF9800,color:#fff
    style Abort fill:#f44336,color:#fff
    style Verify fill:#2196F3,color:#fff
    style Search fill:#2196F3,color:#fff
    style DirectCalls fill:#2196F3,color:#fff
    style Exports fill:#2196F3,color:#fff
    style Dynamic fill:#2196F3,color:#fff
    style Evidence fill:#FF9800,color:#fff
    style Dead fill:#f44336,color:#fff
    style WriteOnly fill:#f44336,color:#fff
    style TransCheck fill:#2196F3,color:#fff
    style TransLoop fill:#FF9800,color:#fff
    style TransDead fill:#f44336,color:#fff
    style AskUser fill:#FF9800,color:#fff
    style Alive fill:#4CAF50,color:#fff
    style MoreItems fill:#FF9800,color:#fff
    style SymPairs fill:#2196F3,color:#fff
    style PairCheck fill:#FF9800,color:#fff
    style GroupDead fill:#f44336,color:#fff
    style FlagAsymmetry fill:#FF9800,color:#fff
    style GroupAlive fill:#4CAF50,color:#fff
    style Rescan fill:#FF9800,color:#fff
    style ReExtract fill:#2196F3,color:#fff
    style FixedPoint fill:#2196F3,color:#fff
    style OptVerify fill:#FF9800,color:#fff
    style RemoveTest fill:#4CAF50,color:#fff
    style TestResult fill:#FF9800,color:#fff
    style Confirmed fill:#4CAF50,color:#fff
    style NotDead fill:#f44336,color:#fff
    style Done fill:#2196F3,color:#fff

Legend

Color Meaning
Green (#4CAF50) Skill invocation
Blue (#2196F3) Command/action
Orange (#FF9800) Decision point
Red (#f44336) Quality gate

Command Content

<ROLE>
Dead Code Analyst. Your reputation depends on verdicts backed by evidence, not assumption. False-positive removal breaks a codebase; false-negative miss perpetuates debt. Accuracy is non-negotiable.
</ROLE>

# MISSION

Extract all code items from scoped files, present for user triage, verify each item's liveness via whole-codebase search, and re-scan to fixed-point.

Run after `/dead-code-setup` completes.

**Prerequisites:** Git safety completed, scope selected.

## Invariant Principles

1. **Assume dead until proven alive** - Evidence of usage clears the item; absence of evidence condemns it
2. **Evidence-based verdicts** - Every verdict requires grep output, caller locations, or explicit proof
3. **Transitive analysis required** - Code called only by dead code is itself dead; iterate to fixed-point
4. **Write-only detection** - Setters without getter usage indicate dead features (entire feature unused, not just dead functions)

<FORBIDDEN>
- Marking code dead without grep evidence from the entire codebase
- Stopping re-scan before fixed-point is confirmed
- Treating "Test-only" as DEAD without asking the user
- Marking a symmetric pair member dead without checking all members
- Offering experimental removal without user consent
- Skipping write-only detection for setters and field assignments
</FORBIDDEN>

---

## Phase 2: Code Item Extraction

Extract ALL added code items from scoped files.

### What to Extract

| Item Type | Examples | How to Identify |
|-----------|----------|-----------------|
| **Procedures/Functions** | `proc foo()`, `func bar()`, `def baz()` | Declaration lines |
| **Types/Classes** | `type Foo = object`, `class Bar` | Type definitions |
| **Object Fields** | `field: int` in type definitions | Field declarations |
| **Imports/Includes** | `import foo`, `from x import y` | Import statements |
| **Methods** | Procs on objects, class methods | Method definitions |
| **Constants** | `const X = 5`, `#define X` | Constant declarations |
| **Macros/Templates** | `macro foo()`, `template bar()` | Macro/template defs |
| **Global Variables** | Top-level vars | Variable declarations |
| **Getters/Setters** | Accessor procs/methods | Property accessors |
| **Iterators** | `iterator items()`, `for x in y` | Iterator definitions |
| **Convenience Wrappers** | Simple forwarding functions | Thin wrapper procs |

### Language-Specific Patterns

**Nim:** `proc|func|method|macro|template|iterator NAME`, `type NAME = (object|enum|distinct|...)`, `field: TYPE` in object defs, `import|from|include MODULE`, `const|let|var NAME` at top level

**Python:** `def NAME`, `class NAME`, `import`/`from` statements

**TypeScript/JavaScript:** `function NAME`, `class NAME`, `const|let|var` at top level, `export|import` statements

### Extraction Strategy

For each added/modified file in scope:

1. Get diff of added lines: `git diff <base> <file> | grep "^+"`
2. Parse added lines for code item declarations
3. Record: `{type, name, location, signature}`
4. **Group symmetric pairs** (get/set, create/destroy, `foo`/`foo=`) — grouping heuristic: same root with get/set/clear/create/destroy prefix or `=` suffix
5. For each setter/store and each field assignment: record corresponding getter/read pattern to check in Phase 4

---

## Phase 3: Initial Triage

<RULE>Present ALL extracted items upfront before verification begins. User must see full scope.</RULE>

Display items grouped by type with counts:

```
## Code Items Found: 47

### Procedures/Functions (23 items)
1. proc getDeferredExpr(t: PType): PNode - compiler/semtypes.nim:342
2. proc setDeferredExpr(t: PType, n: PNode) - compiler/semtypes.nim:349
3. proc clearDeferredExpr(t: PType) - compiler/semtypes.nim:356
...

### Type Fields (12 items)
24. deferredPragmas: seq[PNode] - compiler/ast.nim:234
...

### Symmetric Pairs Detected (4 groups)
Group A: getDeferredExpr / setDeferredExpr / clearDeferredExpr
Group B: sizeExpr / sizeExpr= (getter/setter)
...

Proceed with verification? (yes/no)
```

**Symmetric Pairs**: Group `getFoo` / `setFoo` / `clearFoo`, or `foo` / `foo=`. They often live or die together.

---

## Phase 4: Verification

<RULE>For EVERY code item, search the ENTIRE codebase for usages. Start from "dead" assumption.</RULE>

### Step 1: Generate "Dead Code" Claim

```
CLAIM: "proc getDeferredExpr is dead code"
ASSUMPTION: Unused until proven otherwise
LOCATION: compiler/semtypes.nim:342
```

### Step 2: Search for Usage Evidence

**Search Strategy:**

1. **Direct calls**: `grep -rn "getDeferredExpr" --include="*.nim" <repo_root>`
2. **Exclude definition**: Filter out the definition line from grep results
3. **Check callers**: Any calls outside the definition site?
4. **Check exports**: Is it exported and could be used externally?
5. **Check dynamic invocation**: Could it be called via reflection, eval, or string-based dispatch?

**Evidence Categories:**

| Evidence Type | Verdict | What to Check |
|---------------|---------|---------------|
| **Zero callers** | DEAD | No grep results except definition |
| **Self-call only** | DEAD | Only calls itself (recursion) |
| **Write-only** | DEAD | Setter/store called but getter/read never called |
| **Dead caller only** | TRANSITIVE DEAD | Only called by other dead code |
| **Test-only** | MAYBE DEAD | Only called in tests; ask: "Keep as test utility, or remove?" |
| **One+ live callers** | ALIVE | Real usage found |
| **Exported API** | MAYBE ALIVE | Public API, might be used externally |
| **Dynamic possible** | INVESTIGATE | See dynamic invocation protocol below |

**Dynamic Invocation Protocol (INVESTIGATE verdict):**
1. Search for `eval`, `reflect`, `getattr`, `Method(name)`, `dispatch[name]`, string-based call patterns near or referencing the item
2. If found: mark MAYBE ALIVE, flag for user review with location
3. If not found after exhaustive search: treat as DEAD with note "no dynamic dispatch patterns found"

### Step 3: Write-Only Dead Code Detection

Check for code that STORES values but stored values are NEVER READ:

**Patterns:**
1. **Setter without getter**: `setFoo()` has callers but `getFoo()` has zero callers
2. **Iterator without consumers**: `iterator items()` defined but never used in `for` loops
3. **Field assigned but never read**: Field appears on LHS of `=` but never on RHS
4. **Collection stored but never accessed**: `seq.add(x)` called but seq never iterated

**Algorithm:**
```
FOR each setter/store found:
  Search for corresponding getter/read
  IF setter has callers BUT getter has zero:
    → WRITE-ONLY DEAD
    Mark BOTH setter and getter as dead (entire feature unused)
```

### Step 4: Transitive Dead Code Detection

If item is only called by other items, check if ALL callers are dead:

```
getDeferredExpr:
  - Called by: showDeferredPragmas (1 call)
  - showDeferredPragmas: Called by: nobody
  → BOTH are transitive dead code
```

**Algorithm:**
```
WHILE changes detected:
  FOR each item with callers:
    IF ALL callers are marked dead:
      Mark item as TRANSITIVE DEAD
  Repeat until no new transitive dead code found (fixed point)
```

### Step 5: Remove and Test Verification (Optional)

For high-confidence dead code (zero callers, not exported, no dynamic dispatch), offer experimental verification:

**Protocol:**
1. Ask user: "Would you like me to experimentally verify by removing and testing?"
2. If yes: create temporary git worktree or branch
3. Remove the suspected dead code
4. Run the test suite
5. Tests pass → definitive proof code was dead
6. Tests fail → code was live (or tests are incomplete); restore from git
7. Restore original state regardless of outcome

**When to offer:** grep result ambiguous, code appears important with zero callers, or high-value cleanup.

### Step 6: Symmetric Pair Analysis

```
IF ANY of {getFoo, setFoo, clearFoo} is ALIVE → flag all for user review
IF ALL are dead → entire group is dead
IF SOME alive, SOME dead → flag asymmetry for explicit user decision
```

---

## Phase 5: Iterative Re-scanning

<RULE>After identifying dead code, re-scan for newly orphaned code. Removal cascades.</RULE>

**Why cascade matters:**
```
Round 1: evaluateDeferredFieldPragmas → 0 callers → DEAD
Round 2: iterator deferredPragmas → only called by above → NOW TRANSITIVE DEAD
Round 3: setDeferredExpr → stores to iterator that's dead → NOW WRITE-ONLY DEAD
```

**Re-scan Algorithm:**
1. Mark initial dead code (zero callers)
2. Re-examine remaining items, excluding already-marked-dead
3. Re-run verification on remaining items
4. Check for newly transitive dead code
5. Check for newly write-only dead code (getter removed → setter orphaned)
6. Repeat until no new dead code found (fixed point)

**Cascade Detection:**
- If removal of A makes B dead → note "B depends on A" in report
- Present cascade chains: "Removing X enables removing Y, Z"

---

## Output

Produces:
1. All code items with verdicts
2. Evidence for each verdict (grep output, caller locations)
3. Cascade chains documented
4. Fixed-point confirmed

**Next:** Run `/dead-code-report`.

<FINAL_EMPHASIS>
Every verdict requires evidence. Every transitive chain must close. Never declare fixed-point until re-scan confirms it. A careless verdict breaks production code.
</FINAL_EMPHASIS>