Languages / C#
C# and .NET code analysis: unused code, call graphs and complexity
Roslyn analyzers (IDE0051, IDE0052) and Rider or ReSharper find unused private members inside a .NET solution. repowise finds unused types and files across the solution: it reads every .csproj and .sln, follows ProjectReferences and global usings, treats ASP.NET controllers, minimal API handlers and assembly-scanned handlers as live, and ranks each finding by git history.
Last reviewed: November 2026
Full tier
What repowise detects in C#
Checked against the engine source. Every row runs locally with zero LLM calls.
| Files parsed | .cs, plus Razor and Blazor components projected to C# |
|---|---|
| Import resolution | Full. Namespace map from every .csproj and .sln, implicit and global usings, ProjectReference order, NuGet PackageReference nodes |
| Call graph | Resolved calls, inheritance and interface implementation |
| Dead code | Name-granular reachability through the project graph; unreachable files and unused types |
| Health markers | Complexity, class metrics, Extract Method plans |
| Performance markers | EF Core, Dapper and ADO.NET queries inside foreach, ToListAsync and peers in loops, .Result and .Wait() inside async methods |
| Coverage reports | Cobertura from coverlet, lcov, Clover |
| Frameworks recognised | ASP.NET controllers and MapGet handlers, DbContext to entity files, EF Core configurations, FastEndpoints, MediatR, MassTransit and NServiceBus handlers, Azure Functions, BenchmarkDotNet, source generators |
Specific to this language
How C# code is read
The solution is the unit
repowise parses every .csproj and .sln into a namespace-to-file map and resolves a using in the same project first, then in ProjectReference'd projects. Implicit and global usings are collected per project. Unresolved namespaces that match a PackageReference become NuGet nodes instead of guesses.
Assembly scanning is modelled
Types a framework discovers by scanning the assembly are linked only when the base type is declared and a registration call exists: EF Core ApplyConfigurationsFromAssembly, FastEndpoints, MediatR handlers, MassTransit consumers and NServiceBus handlers. Azure Functions, WCF service contracts, BenchmarkDotNet benchmarks and source generators are roots.
Rescue only, never accuse
C# reachability only rescues files: a file is live when a type it declares is named in a file that can see it through the project graph, extension methods count as use, and assembly attributes or global using keep a file live. A name in a string counts; a name in a comment does not.
Honest comparison
Native C# tools, and when they are enough
| Tool | What it covers |
|---|---|
| Roslyn analyzers (IDE0051, IDE0052, IDE0060) | Unused private members and parameters, with code fixes in Visual Studio and dotnet build. |
| ReSharper and Rider | Solution-wide analysis that finds unused public types and members, in the IDE. |
| Visual Studio code metrics | Cyclomatic complexity, maintainability index, class coupling per method and type. |
| NDepend | Dependency graphs, rules written as queries, technical-debt estimates for .NET. |
| coverlet | Coverage for dotnet test; writes Cobertura, which repowise reads. |
Where repowise adds value
- Headless, repository-wide dead code with confidence and owners, in CI or for an agent, without opening the IDE.
- Framework roots for ASP.NET, EF Core and assembly-scanned handlers, so they are not reported.
- Git history: hotspots, ownership and co-change across the solution.
- One graph with the TypeScript frontend or Python services in the same repository.
How to run it
Analyse a C# repository
From the repository root. Agents get the same results through the get_dead_code, get_health and get_context MCP tools.
uv tool install repowise # or: pipx install repowise
repowise init --yes --no-prose # index the repo, no API key
repowise dead-code --min-confidence 0.7 --safe-only
repowise health --refactoring-targets
dotnet test --collect:"XPlat Code Coverage"
repowise coverage add TestResults/<guid>/coverage.cobertura.xml
repowise serve # graph, health and dead code in a local dashboardQuestions
What people ask before they try it.
How do I find unused code in a C# solution?
Inside the IDE, Rider or ReSharper solution-wide analysis is exact. For a headless report across the whole repository, run repowise dead-code: it reads .csproj and .sln files, follows ProjectReferences and reports unreachable files and unused types with a confidence and the primary owner.
Will ASP.NET controllers be reported as unused?
No. Classes marked [ApiController] or named *Controller and anchored by MapControllers, minimal API MapGet handlers and DbContext entity sets are linked as framework edges.
Does it read coverlet output?
Yes. dotnet test with the XPlat Code Coverage collector writes coverage.cobertura.xml; repowise coverage add reads it.
Does repowise send my C# code to an LLM?
No. Parsing, the call graph, dead code, health scores, performance markers and git history all run locally with zero LLM calls. repowise init --yes --no-prose needs no API key, and the analysis itself makes no network calls. Written documentation prose is the one opt-in step that uses a model, and it can run on a local Ollama model.
Related reading
Index a C# repository.
Public repositories are free on repowise.dev. The CLI is open source and runs on your machine with no API key.