UnusedSymbolAnalyzer 1.0.4

dotnet tool install --global UnusedSymbolAnalyzer --version 1.0.4
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local UnusedSymbolAnalyzer --version 1.0.4
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=UnusedSymbolAnalyzer&version=1.0.4
                    
nuke :add-package UnusedSymbolAnalyzer --version 1.0.4
                    

UnusedSymbolAnalyzer

NuGet

A .NET/Roslyn CLI that scans a solution and finds public classes, methods, and const/static readonly fields that are never referenced anywhere in the solution — candidates for narrowing down to internal. Goes beyond CA1515's type-level check by working at the method/const level with real solution-wide reference counting via SymbolFinder.FindReferencesAsync.

Requirements

  • .NET SDK 10.0 or later must be installed on any machine that runs this tool (not just the runtime). The tool uses Microsoft.Build.Locator to find MSBuild.dll inside an installed SDK in order to open solutions/projects — a bare runtime install won't have that.
  • The analyzed solution can target any TFM (e.g. net8.0, net48) — Roslyn's syntax/semantic analysis is TFM-agnostic and the .NET 10 SDK can restore/evaluate older-TFM projects without needing that older SDK installed. Exception: if the analyzed solution has a global.json pinning an exact SDK version with "rollForward": "disable", that specific SDK must be present.

Install

Published on nuget.org:

# global
dotnet tool install --global UnusedSymbolAnalyzer

# local (pinned per-repo via a tool manifest, good for sharing with teammates)
dotnet new tool-manifest
dotnet tool install --local UnusedSymbolAnalyzer

Or build and pack locally from source:

dotnet pack src/UnusedSymbolAnalyzer.Cli -c Release -o ./nupkg
dotnet tool install --global --add-source ./nupkg UnusedSymbolAnalyzer

Usage

unused-symbols --solution MySolution.sln [--format console|json|markdown] [--config analyzer.config.json]

(--solution also accepts the newer .slnx format.)

Progress/diagnostic output (project count, MSBuild workspace-open failures) goes to stderr; the report itself goes to stdout, so --format json output is safe to pipe.

--dump-symbols prints every collected public symbol (used and unused, with reference counts) rather than the filtered unused-only report — useful for sanity-checking why a symbol was or wasn't flagged.

JSON report schema

[
  {
    "DisplayName": "MyLib.Widget.NeverCalledMethod()",
    "Kind": "Class | Method | Field",
    "ProjectName": "MyLib",
    "FilePath": "C:\\...\\Widget.cs",
    "Line": 12,
    "Category": "Unused"
  }
]

Only genuinely unused symbols (Category: "Unused") appear in the default report; whitelisted symbols are excluded entirely (use --dump-symbols to see them with their whitelist category instead).

Whitelist configuration (analyzer.config.json)

Reflection-based frameworks (ASP.NET Core action invocation, DI container resolution, Quartz.NET job scheduling, ORM entity mapping, JSON/XML serialization) reference members in ways Roslyn's static reference search can't see, which would otherwise produce a pile of false positives. The following are auto-whitelisted and excluded from the unused report, tagged with the matching category instead:

Category Rule
TestProject Symbol's project name contains one of TestProjectNamePatterns
InterfaceOrOverride Symbol is an override, or implements an interface member (explicit or implicit)
AttributeWhitelist Symbol (or its containing type) carries an attribute named in AttributeNames
BaseTypeWhitelist Symbol's containing type derives from / implements a type named in BaseTypeNames
NamespaceWhitelist Symbol's namespace starts with a pattern in NamespacePatterns

Default config (used when no analyzer.config.json is found):

{
  "BaseTypeNames": ["Controller", "ControllerBase", "IJob"],
  "AttributeNames": ["Obsolete", "UsedImplicitly"],
  "NamespacePatterns": [],
  "TestProjectNamePatterns": ["Tests", "Test"]
}

Place a project-specific analyzer.config.json next to where you run the tool (or pass --config <path>) to add your own base classes (e.g. an NHibernate entity base class), attributes, or namespace patterns.

Note: BaseTypeNames matches by simple type name only (not fully-qualified), so it's namespace-agnostic — convenient for matching e.g. any Controller regardless of which Controller base you're actually using, but it means an unrelated type that happens to share the name would also get whitelisted.

Known limitations

  • Static analysis only: reflection-based access that doesn't match any whitelist rule can still produce false positives.
  • DI-container-registered interface implementations aren't specifically whitelisted beyond the general interface-implementation rule.
  • Mapping-config-driven ORM usage (e.g. NHibernate XML/Fluent mappings) isn't parsed — whitelist by base class/attribute instead.
  • Source-generated code is included in the compilation (so it doesn't break analysis) but isn't filtered out of the report by generated-file path patterns yet.

Versioning and distribution

Package version is derived automatically from git tags via MinVer (e.g. tag v1.2.0 → package version 1.2.0; without a tag you get a 0.0.0-alpha.0.<height> pre-release version).

.github/workflows/publish.yml builds, tests, and packs on every v* tag push and uploads the .nupkg as a workflow artifact. From there, pick whichever distribution channel fits your network:

  • Local folder / network share: download the artifact and --add-source it, as shown in Install above.
  • Private feed (Azure Artifacts, private NuGet server): dotnet nuget push the package to your feed.
  • GitHub Packages: push there and add the feed to nuget.config on the consuming machine.
  • nuget.org: works if the target network allows outbound access to it.

For teams, prefer the local tool manifest approach (dotnet new tool-manifest + dotnet tool install --local) and commit .config/dotnet-tools.json, so the tool version is pinned per-repo and dotnet tool restore gets everyone onto the same version.

Development

dotnet build
dotnet test

samples/SampleSolution is a small fixture solution with deliberately unused/used/ whitelisted members, exercised by tests/UnusedSymbolAnalyzer.Tests.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
1.0.4 181 7/10/2026
1.0.3 118 7/8/2026
1.0.2 115 7/8/2026
1.0.1 120 7/8/2026