DEV Community

Nayana Agarwal
Nayana Agarwal

Posted on • Edited on

πŸš€ Roslyn UnUsed Code Remover – Clean Up Your .NET Projects with Ease

Have you ever returned to a .NET project only to find it cluttered with unused codeβ€”methods, variables, or even entire classes that serve no purpose anymore? If so, you're not alone. As projects grow and evolve, it's easy for dead code to accumulate, quietly bloating your codebase and making maintenance harder.

That's why I built Roslyn Unused Code Removerβ€”a lightweight CLI tool that automatically identifies and removes unused code from your C# projects using the Roslyn compiler platform.


✨ What It Does

This tool leverages the power of Roslyn analyzers to detect unused code elements in .cs files and provides a quick way to clean them up. It's especially helpful for:

  • Refactoring old codebases
  • Removing noise before code reviews
  • Keeping things lean and maintainable

βš™οΈ How It Works

Under the hood, the tool:

  1. Parses the solution or project using Roslyn APIs.
  2. Analyzes syntax trees and semantic models to find symbols (variables,function and classes(whose all methods have 0 reference).) that are declared but never referenced.
  3. Comments or flags them for your review which you can later search in folder using "// Remove unused code" .

It's like having an assistant that knows your code well enough to do spring cleaning for you.


πŸ› οΈ Getting Started

Clone the Repo

git clone https://github.com/Nayanaska/Roslyn-Unused-Code-Remover.git
cd Roslyn-Unused-Code-Remover
Enter fullscreen mode Exit fullscreen mode

Build and Run
Make sure you have the .NET SDK installed. Then:

dotnet build
dotnet run
Enter fullscreen mode Exit fullscreen mode

Go to Program.cs file and put path of your solution, the path of log files to check all unused code references that is found and excel file where you want to list down all unused class references under variables solutionPath, LogFilePath and unusedClassesExcelPath and run using command,

dotnet run
Enter fullscreen mode Exit fullscreen mode

The tool will analyze your code and comment and flag unused code blocks.

Code explanation

πŸ“Œ Main Method – The Entry Point

static async Task Main(string[] args)
Enter fullscreen mode Exit fullscreen mode

This is where the program starts.

MSBuildLocator.RegisterDefaults();
Enter fullscreen mode Exit fullscreen mode

Registers MSBuild so Roslyn can load projects and solutions.

string solutionPath = @"Path/to/your/solution";
Enter fullscreen mode Exit fullscreen mode

Sets the path to the .sln file you want to analyze.

 static readonly string unusedClassesExcelPath = @"path\to\save\unusedclassesexcelfile.xlsx";
Enter fullscreen mode Exit fullscreen mode

Sets the path to the .xlsx file you want to save the location of all unused classes.

static readonly string LogFilePath = @"path/of/log/file.txt";
Enter fullscreen mode Exit fullscreen mode

Sets the path to the .txt file you want to save logs in.

🚫 Ignored Folders

static readonly List<string> IgnoredFolders = new List<string>
{
    "Folder1",
    "folder2",
    "folder3"
};
Enter fullscreen mode Exit fullscreen mode

Prevents cleaning test or sample code where unused items may be expected.

using StreamWriter logWriter = new(LogFilePath, append: false);
Console.SetOut(logWriter);
Console.SetError(logWriter);
Enter fullscreen mode Exit fullscreen mode

Redirects all logs and errors to a file for easy review.

using var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(solutionPath);
Enter fullscreen mode Exit fullscreen mode

Creates a Roslyn workspace and loads your solution into it.

var unusedVariables = await FindUnusedVariables(solution);
await CommentOutUnusedVariables(unusedVariables);
Enter fullscreen mode Exit fullscreen mode

Detects and comments out unused variables.

var unusedFunctions = await FindUnusedFunctions(solutionPath);
await CommentOutUnusedFunctions(unusedFunctions);
Enter fullscreen mode Exit fullscreen mode

Same process for methods.

var unusedClasses = await FindUnusedClasses(solutionPath);
await CommentOutUnusedClasses(unusedClasses);
Enter fullscreen mode Exit fullscreen mode

And for classes.

πŸ” FindUnusedVariables

var variables = root.DescendantNodes()
    .OfType<VariableDeclaratorSyntax>()
    .Select(v => new { Symbol = semanticModel.GetDeclaredSymbol(v), SyntaxNode = v });
Enter fullscreen mode Exit fullscreen mode

Finds all variable declarations and fetches their symbols.

var references = await SymbolFinder.FindReferencesAsync(variable.Symbol, solution);
Enter fullscreen mode Exit fullscreen mode

Checks where each variable is used.

if (!references.Any(refs => refs.Locations.Any()))
Enter fullscreen mode Exit fullscreen mode

If there are no references, it’s considered unused.

🧠 FindUnusedFunctions

var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
Enter fullscreen mode Exit fullscreen mode

Extracts all method declarations.

var symbol = semanticModel.GetDeclaredSymbol(method);
Enter fullscreen mode Exit fullscreen mode

Gets the Roslyn symbol for each method.

var refs = await SymbolFinder.FindReferencesAsync(symbol, solution);
Enter fullscreen mode Exit fullscreen mode

Checks if the method is ever called. If notβ€”πŸ’₯ it's unused!

βœ‚οΈ CommentOutUnusedVariables

var unusedItems = unusedVariables.GroupBy(item => item.filePath);
Enter fullscreen mode Exit fullscreen mode

Groups unused variables by their file.

lines[item.lineNumber - 1] = "// Remove unused code" + lines[item.lineNumber - 1];
Enter fullscreen mode Exit fullscreen mode

Comments out the variable declaration directly in the source file.

πŸ”§ CommentOutUnusedFunctions

var methodNode = root.DescendantNodes()
    .OfType<MethodDeclarationSyntax>()
    .FirstOrDefault(n => syntaxTree.GetLineSpan(n.Span).StartLinePosition.Line == item.lineNumber - 1);
Enter fullscreen mode Exit fullscreen mode

Finds the method node based on its line number.

for (int i = startLine; i <= endLine; i++)
{
    updatedLines[i] = "// " + updatedLines[i];
}
Enter fullscreen mode Exit fullscreen mode

Comments out the entire method.

🧹 FindUnusedClasses

var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
Enter fullscreen mode Exit fullscreen mode

Fetches the class symbol.

var classRefs = await SymbolFinder.FindReferencesAsync(classSymbol, solution);
Enter fullscreen mode Exit fullscreen mode

Checks for references to the class itself.

foreach (var member in classSymbol.GetMembers())
Enter fullscreen mode Exit fullscreen mode

Also checks if any member of the class (methods, properties, etc.) is used.

πŸͺ“ CommentOutUnusedClasses

var classNode = root.DescendantNodes()
    .OfType<ClassDeclarationSyntax>()
    .FirstOrDefault(n => ...);
Enter fullscreen mode Exit fullscreen mode

Finds the class node.

updatedLines.Insert(startLine, "// Remove unused code");
Enter fullscreen mode Exit fullscreen mode

Adds a comment before the class.

for (int i = startLine + 1; i <= endLine; i++)
{
    updatedLines[i] = "// " + updatedLines[i];
}
Enter fullscreen mode Exit fullscreen mode

Comments out the entire class block.

πŸ› οΈ BuildSolution

ProcessStartInfo psi = new("dotnet", $"build \"{solutionPath}\"")
Enter fullscreen mode Exit fullscreen mode

Runs dotnet build to ensure the solution is still valid.

return process.ExitCode == 0;
Enter fullscreen mode Exit fullscreen mode

Returns true if build succeeded, false otherwise.

πŸ“€ SaveFailedChangesToExcel & SaveUnusedClassesToExcel
These use EPPlus to export results to Excel.

using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add("Unused Classes");
Enter fullscreen mode Exit fullscreen mode

Creates an Excel file and writes data like:

File path

Line number

Code snippet

Build status

πŸ’‘ Why I Built It
During my development work, I often found myself manually hunting for unused code. I realized this was both time-consuming and error-prone. While tools like ReSharper offer some of this functionality, I wanted a free, scriptable, and open-source solution I could use in CI pipelines or one-off cleanup tasks.

πŸ§ͺ Limitations & Future Plans

Doesn't yet handle dynamically-referenced code or reflection-heavy patterns.

Plans include:

Optional "preview mode"

VS Code integration

CI-friendly reporting output

πŸ™Œ Contributions Welcome
If you're passionate about Roslyn or static code analysis, feel free to open issues or submit PRs. I'd love to collaborate with the community on making this tool smarter and more robust.

πŸ‘‰ Check out the repo

πŸ”š Final Thoughts
Code cleanliness is more than a style preferenceβ€”it's a productivity booster. With Roslyn UnUsed Code Remover, you can keep your C# projects sharp, focused, and free of digital clutter. Give it a try and let me know how it works for you!

Top comments (0)