Skip to content

Dead-code detection flags live code: constructors, dunders, and callback-by-reference all report "0 callers" #1642

Description

@adinballew

Summary

The graph UI's dead-code classification (status: "dead" = "0 callers") flags a large number of live functions/methods as dead. The detector only counts direct CALLS/USAGE/CALL_REFERENCE edges and cannot see three common dispatch patterns, so it reports false positives for:

  1. ConstructorsClassName(...) instantiation never creates a CALLS edge to __init__.
  2. Dunder / framework-invoked methods__str__, __call__, _generate/_agenerate (called by a framework's invoke/ainvoke), _llm_type (property access).
  3. Callback-by-reference — functions passed by name as arguments (asyncio.to_thread(fn, ...), context.configure(include_object=fn), DecisionPoint(apply=fn)).

Repro

On a Python repo, index it and open the graph UI. In my case (a Python monorepo), 50 of 50 "dead" nodes were false positives. Concrete example:

ModeAwareModel.__init__ is flagged dead, but it is instantiated in three places:

# ai/langgraph/services/model.py
def get_model() -> BaseChatModel:
    return ModeAwareModel(ChatOpenAI(...), role="supervisor")

def get_worker_model(session_id=None) -> BaseChatModel:
    return ModeAwareModel(ChatOpenAI(...), role="worker")

# inside bind_tools()
return ModeAwareModel(cast(BaseChatModel, bound), self._role)

The graph has no CALLS edge from ModeAwareModel(...) to ModeAwareModel.__init__, so the constructor is reported as having zero callers.

Other flagged-but-live examples:

  • _apply_updates — passed as apply=_apply_updates to a DecisionPoint.
  • _include_object / _compare_type — passed as include_object= / compare_type= to Alembic's context.configure.
  • _run_file_search — passed to asyncio.to_thread.
  • __str__, __call__, _generate, _agenerate, _llm_type — invoked by runtime/framework dispatch.

Root cause

The classification in src/ui/layout3d.c (cbm_layout_compute):

bool is_fn = label=="Function" || label=="Method";   // only these are candidates
int ic = in_calls[i];
int iu = in_usage[i] + in_call_reference[i];
...
else if (ic==0 && iu==0) status = "dead";

dead is defined purely as zero incoming CALLS/USAGE/CALL_REFERENCE edges. Static analysis cannot resolve constructor dispatch, dunder/framework invocation, or callback-by-reference, so all three produce false "dead" results.

Suggested fix

In the dead-code classification, treat these as live (non-dead):

  • __init__ — live when its owning class has any instantiation (Class(...) call) or is itself referenced/exported.
  • Dunder methods (__str__, __call__, __repr__, __eq__, etc.) — either treat as live, or exclude from dead-code classification entirely.
  • Callback-referenced functions — a function referenced by name as an argument (e.g. asyncio.to_thread(fn), configure(include_object=fn)) should count as having a caller.

At minimum, excluding __init__ and dunders from the "dead" bucket would eliminate the majority of false positives.

Environment

  • codebase-memory-mcp v0.10.4 (single static binary, --ui=true)
  • Python repo, ~13.4k graph nodes

Metadata

Metadata

Assignees

No one assigned

    Labels

    parsing/qualityGraph extraction bugs, false positives, missing edgesux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions