FsClean 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet tool install --global FsClean --version 0.1.0
                    
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 FsClean --version 0.1.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=FsClean&version=0.1.0
                    
nuke :add-package FsClean --version 0.1.0
                    

FsClean

Finds dead code, and unneeded project references, in F# projects, and can remove the dead code.

It type-checks your projects with the F# compiler services, builds a graph of which declarations use which, and reports what nothing live can reach. Because it works by reachability rather than by counting references, it also finds code that is only kept alive by other dead code, including dead cycles that the compiler's unused-value warnings can't see.

Install

dotnet tool install --global FsClean

It needs the .NET 10 SDK. The projects you analyze must be restored first (dotnet restore).

Use

fsclean MyApp.fsproj                     # report dead code
fsclean --whole-program App.fsproj Lib.fsproj Tests.fsproj
fsclean --fix MyApp.fsproj               # remove it

Give it every project that consumes the code you care about. Without --whole-program, the public API of a library counts as used, since something outside the analysis may call it. With it, only entry points and attributed declarations (tests, for instance) are roots, which is what you want for an application and the libraries it owns.

Option
--whole-program Treat only entry points as roots, even in libraries.
--fix Remove the dead code from the source files.
--explain <text> Explain each declaration whose name contains the text: what uses it, what it uses, and what keeps it alive.
--references List every project reference with the uses behind it, not only the ones nothing compiles against.

Removing code

--fix deletes whole declarations together with the comments and attributes directly above them, keeps line endings and byte order marks, and tidies the blank lines. Commit first, so you can review the result with git diff.

Nothing stays removed unless the projects still type-check. A batch that doesn't is split and retried, so one wrong finding costs that finding and not the run. What can't be removed cleanly is left in place, with the reason printed: the first declaration of a let rec ... and or type ... and chain whose later members are live, and the last member of a live type.

Project references

Each <ProjectReference> is judged by whether the referencing project uses anything declared in the referenced one, both by every use and by the uses in live code only. A reference can be unneeded, needed only because dead code uses it, or just the route to a project that should be referenced directly.

What it can't see

Everything here is compile-time evidence. Code that is only reached through reflection, dependency injection, serialization or generated code looks dead, and a project reference that is only needed at runtime (a plugin loaded by reflection, a reference that copies output) looks unneeded. Review what it reports. Initializers that might have side effects are listed for review and never removed.

Development

dotnet build
dotnet run --project tests/FsClean.Tests

The tests run against the small projects in tests/fixtures.

License

MIT

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
0.4.1 109 9/21/2026
0.4.0 112 9/20/2026
0.3.1 101 9/20/2026
0.3.0 99 9/20/2026
0.2.0 104 9/20/2026
0.1.0 106 9/19/2026