| description | Find functions and methods that are unreachable from any entry point using the code knowledge graph. |
|---|
cgr dead-code reports functions and methods that are unreachable from any
entry point in the knowledge graph. It walks the CALLS and REFERENCES edges
outward from a set of roots (exported and public symbols, tests, decorated
handlers such as routes/tasks/CLI commands, dunder/lifecycle methods, and
JavaScript/TypeScript well-known symbol properties) and lists everything the
walk never reaches.
The results are candidates for review, not a guaranteed delete list. Code reached only through dynamic dispatch, reflection, string-keyed lookups, or an external framework that the static graph cannot see may still be reported. Read each candidate before removing it.
Detection needs a call graph, so it covers only the
fully supported languages. Languages
served by the structural (ast-grep) tier, such as Ruby, produce no CALLS
edges, so their symbols are always treated as reachable and never reported.
That exemption is deliberate: without call edges every symbol would look unreachable. But it means a clean report says nothing about those languages, so the command prints how many symbols were skipped:
No unreachable functions or methods found.
12 symbol(s) in structural-tier languages were not analyzed (no call graph for these languages).
Index the repository first, so the graph exists in Memgraph:
cgr daemon up
cgr start --repo-path /path/to/your/repo --update-graph --cleancgr dead-codeIf a single project is indexed it is used automatically. When several are indexed, name one:
cgr dead-code --project-name my-projectA function invoked only by a framework or an external caller has no visible call site, so it looks unreachable. Mark such roots so the code they reach is not reported:
# Treat any symbol whose qualified name ends with these as reachable roots
cgr dead-code -e main -e cli.run -e handlers.webhook
# Treat symbols carrying a decorator as roots (extends the built-in set:
# route, task, fixture, command, ...)
cgr dead-code --decorator-root celery_app.task --decorator-root my_registry.registerGenerated or vendored code (API clients, protobuf stubs) is full of callbacks a library invokes and reports noisily. Exclude it by file-path glob:
cgr dead-code --exclude '*client/core*' --exclude '*.gen.*'Two rules keep a pattern from silently excluding nothing:
- Quote the glob. Unquoted, the shell expands
--exclude src/gen/*into a file listing beforecgrruns — the first file becomes the pattern and the rest are rejected as extra arguments (or, if the glob matches nothing, some shells pass it through and others error out). Quotes make the shell hand the pattern over untouched. - Cover the whole path. Patterns are matched against the full
repo-relative file path (
src/client/core/api.py), and a glob only counts when it matches that entire string. A bare directory name liketestsmatches nothing; spell the path out from the repo root ('tests/*','src/tests/*') and add'*/tests/*'when test directories nest deeper.*spans/, so keep patterns path-scoped: a substring glob like'*tests*'also excludes production paths such ascontests/entry.py.
| Option | Description |
|---|---|
--project-name, -n |
Project to scan. Defaults to the sole indexed project. |
--entry-point, -e |
Treat symbols whose qualified name ends with this value as reachable roots. Repeatable. |
--decorator-root |
Treat symbols carrying this decorator as roots. Extends the built-in set. Repeatable. |
--exclude |
Glob matched against a symbol's whole repo-relative file path to exclude it from the report; quote it. Repeatable. |
--include-tests / --no-include-tests |
Treat test code as reachable roots so the production code it exercises is not reported. On by default. |
--classes / --no-classes |
Also report unreachable classes. Off by default. |
--format |
Output format: table (default) or json. |
--output, -o |
Write the report to this file instead of stdout. |
--fail-on-found |
Exit with code 1 when any candidate is found (useful in CI). |
Fail a build when new unreachable code appears, writing a JSON report for the job artifacts:
cgr dead-code --format json --output dead-code.json --fail-on-found \
--exclude '*_generated*'- Roots: exported/public symbols, tests (unless
--no-include-tests), decorated handlers, dunder/lifecycle methods, plus any--entry-pointand--decorator-rootyou add. - Reachability: a breadth-first walk over
CALLSandREFERENCESedges from every root. With--classesthe walk also followsINSTANTIATESandINHERITS, so a class counts as reachable when a reachable class instantiates or subclasses it. - Report: functions and methods (and, with
--classes, classes) the walk never reaches, minus anything matching an--excludeglob.
First-class functions matter for accuracy: a callback stored in an object, an
inline arrow handed to useMutation/.forEach/new Promise, or a function
passed as an argument is recorded as a REFERENCES edge so it stays reachable
rather than being reported as dead.