How Dead Code Detection Works in Codebase-Memory-MCP: Graph Analysis Explained
Codebase-Memory-MCP identifies dead code by querying inbound degrees of CALLS and USAGE edges in its SQLite-backed dependency graph, classifying functions with zero incoming connections as dead while exempting tests, entry points, and exported symbols.
Codebase-Memory-MCP (MCP) builds a complete graph representation of your codebase where functions, methods, and classes serve as nodes connected by semantic edges. Dead code detection operates during the 3D layout phase in src/ui/layout3d.c, where the system analyzes the full graph to determine which functions are reachable and which are orphaned.
The Dead Code Classification Matrix
MCP applies a strict hierarchy of rules to classify each node during the layout phase. The classification depends on node type, metadata flags, and inbound connection counts from the graph store:
- Structural: Non-function and non-method nodes (classes, variables, etc.)
- Test: Nodes located in test files (identified by
is_testflags or path heuristics) - Entry: Entry points marked with
is_entry_pointorroute_pathflags - Exported: Symbols explicitly marked with
is_exportedin their properties - Dead: Functions or methods with zero inbound
CALLSand zero inboundUSAGE - Single: Functions or methods with exactly one inbound
CALLSedge - Normal: All other function/method nodes with multiple connections
Dead Code Detection Implementation in layout3d.c
The dead code detection algorithm executes in five distinct phases within the layout engine.
1. Node Collection and Batch Preparation
The system first collects all node IDs from the current search result set to prepare for batch degree queries:
int64_t *node_ids = malloc((size_t)n * sizeof(int64_t));
for (int i = 0; i < n; i++)
node_ids[i] = search_out.results[i].node.id;
To avoid SQLite parameter limits, the implementation processes nodes in chunks defined by DEAD_DEGREE_CHUNK.
2. Batch Querying Inbound Degrees
The layout engine queries the store for inbound degrees of both CALLS and USAGE edge types using cbm_store_batch_count_degrees:
/* Allocate buffers for inbound CALLS and USAGE counts */
int *in_calls = calloc((size_t)n, sizeof(int));
int *in_usage = calloc((size_t)n, sizeof(int));
/* Query the store in chunks to avoid SQL limits */
for (int off = 0; off < n; off += DEAD_DEGREE_CHUNK) {
int cnt = (n - off < DEAD_DEGREE_CHUNK) ? (n - off) : DEAD_DEGREE_CHUNK;
cbm_store_batch_count_degrees(store,
node_ids + off, cnt, "CALLS", in_calls + off, deg_dummy + off);
cbm_store_batch_count_degrees(store,
node_ids + off, cnt, "USAGE", in_usage + off, deg_dummy + off);
}
This batch approach minimizes database round-trips by building bounded IN (?, ?, …) clauses.
3. Parsing Node Metadata Flags
Before classification, the system parses node-specific flags from the JSON properties_json stored with each node:
node_flags_t nf = parse_node_flags(sn->properties_json);
These flags indicate whether the node represents a test file, entry point, exported symbol, or route.
4. Applying the Decision Logic
The core classification logic applies the decision matrix through a cascading conditional chain:
bool is_fn = sn->label && (strcmp(sn->label, "Function") == 0 ||
strcmp(sn->label, "Method") == 0);
int ic = in_calls ? in_calls[i] : 1; // inbound CALLS
int iu = in_usage ? in_usage[i] : 1; // inbound USAGE
const char *status;
if (!is_fn) status = "structural";
else if (testish) status = "test";
else if (nf.is_entry || nf.is_route) status = "entry";
else if (nf.is_exported) status = "exported";
else if (ic == 0 && iu == 0) status = "dead";
else if (ic == 1) status = "single";
else status = "normal";
result->nodes[i].status = status;
5. Error Handling and Fallbacks
If the batch query fails due to SQLite errors, the code defaults to "normal" status rather than incorrectly flagging code as dead. This safety mechanism ensures transient store problems do not result in false dead code positives.
Store-Level Queries for Dead Code Detection
The heavy lifting occurs in cbm_store_batch_count_degrees, implemented in src/store/store.c. This function constructs parameterized SQL queries to count degrees efficiently:
int cbm_store_batch_count_degrees(cbm_store_t *s,
const int64_t *node_ids, int id_count,
const char *edge_type, int *out_in, int *out_out)
{
/* ... build the IN clause like "(?,?,?)" ... */
bool has_type = edge_type && edge_type[0] != '\0';
/* inbound degree */
int rc = count_degrees_direction(s, node_ids, id_count,
in_clause, has_type, edge_type, true, out_in);
if (rc != CBM_STORE_OK) return rc;
/* outbound degree */
return count_degrees_direction(s, node_ids, id_count,
in_clause, has_type, edge_type, false, out_out);
}
The function calls count_degrees_direction twice—once for inbound and once for outbound edges—to populate the degree buffers.
Data Pipeline and Edge Creation
The dead code detection relies on CALLS edges created during the analysis pipeline. The file src/pipeline/pass_calls.c resolves function calls and inserts these edges into the graph, providing the connectivity data that the layout phase later queries to determine liveness.
Summary
- Graph-Based Analysis: MCP detects dead code by examining inbound
CALLSandUSAGEdegrees in the complete dependency graph. - Dual Edge Types: A function is only dead if both
CALLSandUSAGEinbound counts equal zero. - Exclusion Logic: Tests, entry points, routes, and exported symbols are explicitly protected from dead code classification.
- Batch Processing: Degree queries use chunked batch operations via
cbm_store_batch_count_degreesto handle large codebases efficiently. - Safe Defaults: Store errors default to
"normal"status, preventing false dead code reports.
Frequently Asked Questions
How does Codebase-Memory-MCP define dead code?
Codebase-Memory-MCP defines dead code as any function or method node in the dependency graph that has zero inbound CALLS edges and zero inbound USAGE edges. This means no other function calls it, and no other part of the codebase references it through usage relationships.
What prevents entry points and tests from being marked as dead?
The system checks node metadata flags parsed from properties_json before applying degree-based rules. Nodes marked with is_entry_point, route_path, is_exported, or test file heuristics receive "entry", "exported", or "test" statuses respectively, bypassing the dead code classification regardless of their inbound degree counts.
Where does the dead code detection logic reside in the codebase?
The primary classification logic lives in src/ui/layout3d.c within the layout phase implementation. The underlying degree counting queries are handled by cbm_store_batch_count_degrees in src/store/store.c, while src/pipeline/pass_calls.c generates the CALLS edges that feed the detection algorithm.
What happens if the SQLite query fails during dead code detection?
If cbm_store_batch_count_degrees returns an error, the layout engine falls back to assigning a "normal" status to affected nodes. This conservative approach ensures that temporary database issues or connectivity problems do not cause the system to falsely report functioning code as dead.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →