roe dead-code reports code that’s unreachable from your solution’s entry
points. Entry points are never flagged themselves, and reachability is
traced outward from them.
What counts as an entry point
static Mainand top-level-statement files- ASP.NET controllers (
*Controller/ControllerBase) and their actions,Startupconventions - Test methods and fixtures (
[Fact],[Theory],[Test],[TestMethod], …) - Any declaration carrying a non-inert attribute (
[HttpGet],[JsonProperty],[UsedImplicitly], custom markers — everything except[Obsolete]-class metadata) - Reflection-scan contracts: implementations of a type used as a lone
generic argument in a call (
GetExports<IImageProvider>(),AddHostedService<W>()) - Public API surface of NuGet-packable projects, of every non-test project
when the workspace has no executable (“library mode”), and of any project
named with
--library/libraryProjects(a project consumed outside the workspace, e.g. a Unity project referencing the built DLL, regardless of what executables live alongside it) - Declarations in generated files (
*.g.cs,*.Designer.cs,<auto-generated>headers,obj/) — reference-only, never flagged
Adding your own roots
When code is only reached in ways roe can’t see — string-based reflection, an external plugin host — declare it as a root with the--root flag or the
roots config field, using the symbol’s fully-qualified name:
roe.json
entryPoints config field instead of listing every symbol in it. Patterns
are globs resolved relative to the config file’s directory (a trailing /
matches the whole directory); every declaration in a matching file becomes
a root, so the file and everything it references count as used:
roe.json
How detection works
Under the hood,roe dead-code runs a five-step pipeline:
- Discover — parses
.slnand.csprojfiles and walks the source tree, gitignore-aware, harvestingobj/generated sources as reference-only inputs. - Extract — parses every
.csfile in parallel with tree-sitter, collecting declarations and references. - Resolve — merges partial types and overloads into one symbol table; references resolve by namespace-aware, conservative name matching.
- Mark and sweep — walks the reference graph outward from the entry points. A member only counts as used when its name is referenced from reachable code and its containing type is reachable.
- Report — unreachable symbols become findings; a file whose every declaration is dead is reported once as a dead file.