Skip to content

mblink/find-unused

Repository files navigation

Find Unused

This is a Scala 3 tool to find unused code. It uses the TASTy files generated by the compiler and tasty-query to determine what code is used/unused.

See below for more details on setup and use.

Install and configure

sbt plugin

To install the tool in an sbt project, add this to project/plugins.sbt, using the version number from the latest GitHub release:

GitHub Release

resolvers += "bondlink-maven-repo" at "https://maven.bondlink-cdn.com"
addSbtPlugin("bondlink" % "find-unused" % "x.y.z")

Then update build.sbt to specify which top-level packages should be analyzed for unused code:

Global / findUnusedPackages := Seq("foo", "bar")

Standalone CLI

To install the tool as a standalone CLI, download find-unused.jar from the latest GitHub release.

Usage

sbt plugin

A few sbt commands are available:

Command Description
findUnusedExplicits Analyze the configured packages for unused explicit (i.e. non-implicit/given/using) code
findUnusedGivens Analyze the configured packages for unused implicit/given/using code
findUnusedImplicits An alias for findUnusedGivens
findUnusedAll Analyze the configured packages for all types of unused code

Standalone CLI

You can run the standalone CLI with:

java -jar /path/to/find-unused.jar

There are a couple options you must pass, as well as some optional ones:

Option Type Description
--package Required, repeated The packages to analyze
--classpath Required, repeated The full classpath of your code and all dependencies
--exclusion Optional, repeated Code to exclude from unused warnings, see Exclusions
--root-directory Optional The path to the root of your codebase; used when reporting code positions

Example usage

java -jar /path/to/find-unused.jar \
  --root-directory /path/to/your/code \
  --package com.example.foo \
  --package com.example.bar \
  --classpath "$HOME/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.7.3/scala3-library_3-3.7.3.jar" \
  --classpath ... \
  --exclusion 'src=.*/src_managed/.*' \
  --exclusion 'sym=^com\.example\.MyClass\.(foo|bar)$'

Exclusions

You may wish to exclude some code from being reported as unused. The tool supports two types of exclusions:

Name Description
src Exclude all code in files that match the given regex
sym Exclude all code whose symbols match the given regex

Usage in sbt plugin

You can define exclusions using the findUnusedExclusions setting:

Global / findUnusedExclusions := Seq(
  FindUnusedExclusion(src = ".*/src_managed/.*".r),
  FindUnusedExclusion(sym = """^com\\.example\\.MyClass\\.(foo|bar)$""".r),
  FindUnusedExclusion(src = "...".r, sym = "...".r),
)

Known issues

givens/implicits summoned with inline/macro methods

As of Scala 3.7.2, the TASTy representation of some inline and macro-related methods does not include references to the given/implicit instances that they summon -- scala/scala3#22701

This includes:

  1. scala.compiletime.summonAll
  2. scala.compiletime.summonFrom
  3. scala.quoted.Expr.summon
  4. Uses of scala.compiletime.summonInline in macros

The end result is that any instances that are only resolved by these methods will be reported as unused.

This is especially problematic for derivation, which often uses summonFrom. For example, with this code using circe derivation:

import io.circe.Decoder

case class Foo(int: Int) derives Decoder
case class Bar(foo: Foo) derives Decoder

Foo's derived Decoder will be reported as unused because circe derivation uses summonFrom. The Decoder is required and used by Bar's derived Decoder.

transparent inline methods

As of Scala 3.7.2, the TASTy representation of calls to transparent inline methods is equivalent to the TASTy representation of the body of the transparent inline method -- there is no indication that the transparent inline method was called.

For example:

transparent inline def foo(): Int = 1
val bar = foo()

The TASTy representation for val bar does not include a reference to transparent inline def foo, it appears as if val bar is simply defined as

val bar = 1

The end result is that transparent inline defs are reported as unused even when they are in fact used.

Lack of support for multiple Scala versions

At the moment this tool only works when all projects in your build use the same Scala version, and that version is Scala 3.7.x.

Support may be added in the future for multiple Scala versions within the same build or for older Scala 3 versions.

Debugging

You can use the sbt plugin to generate a launch.json config file that can be used in Visual Studio Code to debug find-unused.

To debug a project that uses the sbt plugin:

  1. Load sbt in the project directory
  2. Run one of the available sbt commands: findUnusedExplicitsDebugConfig, findUnusedGivensDebugConfig, or findUnusedAllDebugConfig
  3. Copy the JSON that's printed out
  4. Clone find-unused
  5. Create /path/to/find-unused/.vscode/launch.json using the JSON from step 3
  6. Open the find-unused directory in VS Code
  7. Install and setup Metals
  8. Import the sbt build with Metals and wait for completion
  9. Set any breakpoints you want in find-unused and start debugging

About

Scala 3 tool to find unused code

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 2

  •  
  •  

Languages