# How Refactoring Tools Detect Dead Code and Suggest Renames in code-review-graph

> Learn how code-review-graph refactoring tools detect dead code using DFS and suggest renames with Levenshtein distance analysis. Optimize your codebase efficiently.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: internals
- Published: 2026-07-30

---

**The refactoring subsystem in `code-review-graph` constructs a language-agnostic call graph, performs DFS-based reachability analysis from entry points to isolate unreachable definitions as dead code, and computes Levenshtein-distance similarity scores on identifiers to suggest context-aware renames that are validated against import edges before application.**

The `code-review-graph` repository provides automated refactoring capabilities for multi-language codebases. Its **refactoring tools detect dead code and suggest renames** by analyzing a centralized graph representation that connects definitions, references, and imports across the entire repository.

## Building the Language-Agnostic Call Graph

Dead-code detection and rename accuracy depend on a comprehensive graph structure built by the parsing subsystem.

### Parsing and Graph Population

Language-specific parsers extract symbols and references from source files. The [`parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/parser.py) module orchestrates this process, while [`jedi_resolver.py`](https://github.com/tirth8205/code-review-graph/blob/main/jedi_resolver.py) handles Python-specific resolution using the Jedi library. These components populate [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py), which stores nodes for functions, classes, and variables, along with edges representing calls, imports, and references.

### Entry Point Identification

The `find_dead_code` function in [`code_review_graph/refactor.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/refactor.py) identifies entry points as the starting nodes for reachability analysis. These include exported symbols, `main` functions, test suites, and declared public APIs. Any definition not reachable from these roots is a candidate for dead-code elimination.

## Detecting Dead Code via Reachability Analysis

Once the graph is constructed, the system applies graph traversal algorithms to distinguish active code from orphaned definitions.

### DFS Traversal in find_dead_code

The `find_dead_code` function implements a depth-first search (DFS) over the graph’s adjacency lists beginning at entry points. As the traversal visits each node, it marks reachable definitions. After completion, any definition node remaining unmarked is classified as dead code. According to the `code-review-graph` source code, this function filters out false positives—such as symbols accessed via reflection or external entry points—using language-specific heuristics before reporting.

### Reporting Dead Code

The detector returns a structured list containing file paths, line numbers, and descriptions of unreachable symbols. This data powers the CLI’s `detect_dead_code` command and generates suggestions for pull request reviews.

## Suggesting Renames with Context Awareness

The rename engine analyzes identifier patterns and validates potential changes against the graph to prevent breaking changes.

### Identifier Similarity Scoring

The `suggest_refactorings` function in [`code_review_graph/refactor.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/refactor.py) extracts all identifier names from the graph and computes similarity scores using a combination of **Levenshtein distance** and token-level matching that understands naming conventions like `camelCase` and `snake_case`. High-scoring pairs indicate potential renaming opportunities where one name better describes the underlying logic.

### Collision Prevention via Graph Constraints

Before suggesting a rename, the engine consults the graph’s import edges and scope information stored in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py). Candidates that would cause name collisions or break existing imports are automatically discarded. This ensures that suggested renames are **syntactically safe** within the specific context of the codebase.

### Preview and Application Workflow

The refactoring workflow supports human-in-the-loop confirmation through a staged preview system:

- **Preview Generation**: `rename_preview` assembles a provisional edit set mapping each rename to its definition and all reference locations. It stores this state in the `_pending_refactors` cache keyed by a short random `refactor_id`.
- **Diff Inspection**: Users retrieve a human-readable diff showing exactly which files and lines will change.
- **Application**: `apply_refactor` validates the preview against the current repository state, writes the edits to disk if validation passes, and cleans up the cache entry.

## Practical Implementation Examples

The following examples demonstrate how to invoke the dead-code detector and rename engine programmatically.

### Detecting Dead Code

```python
from code_review_graph.refactor import find_dead_code

# `store` is a GraphStore instance created by the CLI.

dead_items = find_dead_code(store)

for item in dead_items:
    print(f"Dead: {item.file_path}:{item.lineno} – {item.kind}")

```

### Generating Rename Suggestions

```python
from code_review_graph.refactor import suggest_refactorings

suggestions = suggest_refactorings(store)

for s in suggestions:
    print(f"Rename `{s.old_name}` → `{s.new_name}` in {s.file_path}")

```

### Previewing and Applying Renames

```python
from code_review_graph.refactor import rename_preview, apply_refactor

# Request a preview for a specific rename

preview = rename_preview(
    store,
    edits=[{
        "path": "src/utils.py",
        "old_name": "calc_sum",
        "new_name": "compute_total"
    }]
)

refactor_id = preview["refactor_id"]
print(preview["diff"])      # Human-readable diff

# Apply the changes (set dry_run=True to validate without writing)

result = apply_refactor(refactor_id, repo_root, dry_run=False)
print(result["message"])

```

## Summary

- **[`code_review_graph/refactor.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/refactor.py)** contains the core `find_dead_code` function that performs DFS reachability analysis from entry points to identify unused definitions.
- The **call graph** constructed in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py) and populated by [`parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/parser.py) and [`jedi_resolver.py`](https://github.com/tirth8205/code-review-graph/blob/main/jedi_resolver.py) provides the structural data needed for both dead-code detection and rename validation.
- **Rename suggestions** rely on Levenshtein distance and token matching algorithms, filtered by import edges to prevent collisions.
- The **preview workflow** using `rename_preview` and `apply_refactor` with transient `refactor_id` caching ensures safe, reviewable refactoring operations.
- All analyses are **language-agnostic** at the graph level, with language-specific details handled by dedicated resolvers.

## Frequently Asked Questions

### How does find_dead_code determine which symbols are entry points?

The function identifies entry points by scanning for exported symbols, `main` function definitions, test suite entry points, and explicitly declared public APIs. It then performs a depth-first search from these nodes across the adjacency lists in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py), marking all visited nodes as reachable. Any definition node not visited during this traversal is flagged as potentially dead code.

### What algorithm does the rename suggester use to calculate similarity?

The `suggest_refactorings` implementation computes similarity using a combination of **Levenshtein distance** for string edit distance and token-level matching that splits identifiers on case changes and underscores. This hybrid approach detects similarities between different naming conventions like `calculateTotal` and `calc_total` while filtering out candidates that would conflict with existing imports or scope boundaries.

### Can the refactoring tools handle dynamic language features like reflection?

The tools apply language-specific heuristics in `find_dead_code` to filter false positives that may arise from dynamic features such as reflection or string-based imports. While the static graph cannot fully resolve dynamic runtime behavior, these heuristics exclude symbols likely accessed via non-static paths from dead-code reports, reducing false positives.

### How is the preview state persisted between rename_preview and apply_refactor?

When `rename_preview` generates a refactoring plan, it stores the provisional edit set in an internal `_pending_refactors` dictionary keyed by a randomly generated `refactor_id` string. This transient cache maintains the state until `apply_refactor` validates and applies the changes, at which point the entry is removed. The design allows users to review diffs and confirm changes without locking files indefinitely.